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

404

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

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

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

Usage

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

Options

headerLinkSelector

headerAnchorSelector

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

delay

offset

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

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

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

使用方法

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

配置项

headerLinkSelector

headerAnchorSelector

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

delay

offset

',4);function C(x,E){const c=n("NpmBadge"),l=n("RouterLink"),r=n("ExternalLinkIcon");return d(),t("div",null,[u,a(c,{package:"@vuepress/plugin-active-header-links"}),m,e("ul",null,[v,_,e("li",null,[D,k,e("p",null,[s("你通常不需要设置该选项,除非你通过 "),a(l,{to:"/zh/config.html#markdown-anchor"},{default:o(()=>[s("markdown.anchor")]),_:1}),s(" 修改了 "),e("a",b,[s("markdown-it-anchor"),a(r)]),s(" 的 "),f,s(" 选项。")])]),e("li",null,[y,e("ul",null,[e("li",null,[a(l,{to:"/guide/markdown.html#%E6%A0%87%E9%A2%98%E9%94%9A%E7%82%B9"},{default:o(()=>[s("指南 > Markdown > 语法扩展 > 标题锚点")]),_:1})])])])]),g])}const w=p(h,[["render",C],["__file","active-header-links.html.vue"]]);export{w as default}; diff --git a/assets/active-header-links.html-bF3r-Kk6.js b/assets/active-header-links.html-bF3r-Kk6.js new file mode 100644 index 0000000000..211dd505bf --- /dev/null +++ b/assets/active-header-links.html-bF3r-Kk6.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6981b9b0","path":"/zh/plugins/active-header-links.html","title":"active-header-links","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"headerLinkSelector","slug":"headerlinkselector","link":"#headerlinkselector","children":[]},{"level":3,"title":"headerAnchorSelector","slug":"headeranchorselector","link":"#headeranchorselector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"offset","slug":"offset","link":"#offset","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/active-header-links.md"}');export{e as data}; diff --git a/assets/app-GUhkEPRO.js b/assets/app-GUhkEPRO.js new file mode 100644 index 0000000000..d5ab65439e --- /dev/null +++ b/assets/app-GUhkEPRO.js @@ -0,0 +1,32 @@ +/** +* @vue/shared v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Ao(e,t){const n=new Set(e.split(","));return t?r=>n.has(r.toLowerCase()):r=>n.has(r)}const Le={},cn=[],tt=()=>{},ia=()=>!1,Fn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Po=e=>e.startsWith("onUpdate:"),Se=Object.assign,Ro=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},la=Object.prototype.hasOwnProperty,de=(e,t)=>la.call(e,t),ne=Array.isArray,un=e=>Or(e)==="[object Map]",bi=e=>Or(e)==="[object Set]",le=e=>typeof e=="function",Ce=e=>typeof e=="string",gn=e=>typeof e=="symbol",we=e=>e!==null&&typeof e=="object",Ei=e=>(we(e)||le(e))&&le(e.then)&&le(e.catch),yi=Object.prototype.toString,Or=e=>yi.call(e),aa=e=>Or(e).slice(8,-1),Li=e=>Or(e)==="[object Object]",Oo=e=>Ce(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Rn=Ao(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Cr=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},ca=/-(\w)/g,rt=Cr(e=>e.replace(ca,(t,n)=>n?n.toUpperCase():"")),ua=/\B([A-Z])/g,tn=Cr(e=>e.replace(ua,"-$1").toLowerCase()),Bn=Cr(e=>e.charAt(0).toUpperCase()+e.slice(1)),jr=Cr(e=>e?`on${Bn(e)}`:""),kt=(e,t)=>!Object.is(e,t),Ur=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},fa=e=>{const t=parseFloat(e);return isNaN(t)?e:t},da=e=>{const t=Ce(e)?Number(e):NaN;return isNaN(t)?e:t};let as;const wi=()=>as||(as=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function jn(e){if(ne(e)){const t={};for(let n=0;n{if(n){const r=n.split(pa);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function Ge(e){let t="";if(Ce(e))t=e;else if(ne(e))for(let n=0;nCe(e)?e:e==null?"":ne(e)||we(e)&&(e.toString===yi||!le(e.toString))?JSON.stringify(e,Ai,2):String(e),Ai=(e,t)=>t&&t.__v_isRef?Ai(e,t.value):un(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,o],s)=>(n[Wr(r,s)+" =>"]=o,n),{})}:bi(t)?{[`Set(${t.size})`]:[...t.values()].map(n=>Wr(n))}:gn(t)?Wr(t):we(t)&&!ne(t)&&!Li(t)?String(t):t,Wr=(e,t="")=>{var n;return gn(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};/** +* @vue/reactivity v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Xe;class ba{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Xe,!t&&Xe&&(this.index=(Xe.scopes||(Xe.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=Xe;try{return Xe=this,t()}finally{Xe=n}}}on(){Xe=this}off(){Xe=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n=2))break}this._dirtyLevel<2&&(this._dirtyLevel=0),rn()}return this._dirtyLevel>=2}set dirty(t){this._dirtyLevel=t?2:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let t=St,n=Jt;try{return St=!0,Jt=this,this._runnings++,cs(this),this.fn()}finally{us(this),this._runnings--,Jt=n,St=t}}stop(){var t;this.active&&(cs(this),us(this),(t=this.onStop)==null||t.call(this),this.active=!1)}}function La(e){return e.value}function cs(e){e._trackId++,e._depsLength=0}function us(e){if(e.deps&&e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},vr=new WeakMap,Xt=Symbol(""),uo=Symbol("");function Ye(e,t,n){if(St&&Jt){let r=vr.get(e);r||vr.set(e,r=new Map);let o=r.get(n);o||r.set(n,o=Si(()=>r.delete(n))),Ci(Jt,o)}}function _t(e,t,n,r,o,s){const i=vr.get(e);if(!i)return;let l=[];if(t==="clear")l=[...i.values()];else if(n==="length"&&ne(e)){const a=Number(r);i.forEach((c,u)=>{(u==="length"||!gn(u)&&u>=a)&&l.push(c)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":ne(e)?Oo(n)&&l.push(i.get("length")):(l.push(i.get(Xt)),un(e)&&l.push(i.get(uo)));break;case"delete":ne(e)||(l.push(i.get(Xt)),un(e)&&l.push(i.get(uo)));break;case"set":un(e)&&l.push(i.get(Xt));break}Io();for(const a of l)a&&Ii(a,2);xo()}function wa(e,t){var n;return(n=vr.get(e))==null?void 0:n.get(t)}const Ta=Ao("__proto__,__v_isRef,__isVue"),Di=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(gn)),fs=Aa();function Aa(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=pe(this);for(let s=0,i=this.length;s{e[t]=function(...n){nn(),Io();const r=pe(this)[t].apply(this,n);return xo(),rn(),r}}),e}function Pa(e){const t=pe(this);return Ye(t,"has",e),t.hasOwnProperty(e)}class ki{constructor(t=!1,n=!1){this._isReadonly=t,this._shallow=n}get(t,n,r){const o=this._isReadonly,s=this._shallow;if(n==="__v_isReactive")return!o;if(n==="__v_isReadonly")return o;if(n==="__v_isShallow")return s;if(n==="__v_raw")return r===(o?s?Na:Mi:s?$i:Vi).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(r)?t:void 0;const i=ne(t);if(!o){if(i&&de(fs,n))return Reflect.get(fs,n,r);if(n==="hasOwnProperty")return Pa}const l=Reflect.get(t,n,r);return(gn(n)?Di.has(n):Ta(n))||(o||Ye(t,"get",n),s)?l:je(l)?i&&Oo(n)?l:l.value:we(l)?o?Wn(l):Un(l):l}}class zi extends ki{constructor(t=!1){super(!1,t)}set(t,n,r,o){let s=t[n];if(!this._shallow){const a=pn(s);if(!_r(r)&&!pn(r)&&(s=pe(s),r=pe(r)),!ne(t)&&je(s)&&!je(r))return a?!1:(s.value=r,!0)}const i=ne(t)&&Oo(n)?Number(n)e,Ir=e=>Reflect.getPrototypeOf(e);function Zn(e,t,n=!1,r=!1){e=e.__v_raw;const o=pe(e),s=pe(t);n||(kt(t,s)&&Ye(o,"get",t),Ye(o,"get",s));const{has:i}=Ir(o),l=r?So:n?zo:kn;if(i.call(o,t))return l(e.get(t));if(i.call(o,s))return l(e.get(s));e!==o&&e.get(t)}function er(e,t=!1){const n=this.__v_raw,r=pe(n),o=pe(e);return t||(kt(e,o)&&Ye(r,"has",e),Ye(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function tr(e,t=!1){return e=e.__v_raw,!t&&Ye(pe(e),"iterate",Xt),Reflect.get(e,"size",e)}function ds(e){e=pe(e);const t=pe(this);return Ir(t).has.call(t,e)||(t.add(e),_t(t,"add",e,e)),this}function hs(e,t){t=pe(t);const n=pe(this),{has:r,get:o}=Ir(n);let s=r.call(n,e);s||(e=pe(e),s=r.call(n,e));const i=o.call(n,e);return n.set(e,t),s?kt(t,i)&&_t(n,"set",e,t):_t(n,"add",e,t),this}function ps(e){const t=pe(this),{has:n,get:r}=Ir(t);let o=n.call(t,e);o||(e=pe(e),o=n.call(t,e)),r&&r.call(t,e);const s=t.delete(e);return o&&_t(t,"delete",e,void 0),s}function ms(){const e=pe(this),t=e.size!==0,n=e.clear();return t&&_t(e,"clear",void 0,void 0),n}function nr(e,t){return function(r,o){const s=this,i=s.__v_raw,l=pe(i),a=t?So:e?zo:kn;return!e&&Ye(l,"iterate",Xt),i.forEach((c,u)=>r.call(o,a(c),a(u),s))}}function rr(e,t,n){return function(...r){const o=this.__v_raw,s=pe(o),i=un(s),l=e==="entries"||e===Symbol.iterator&&i,a=e==="keys"&&i,c=o[e](...r),u=n?So:t?zo:kn;return!t&&Ye(s,"iterate",a?uo:Xt),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:l?[u(f[0]),u(f[1])]:u(f),done:d}},[Symbol.iterator](){return this}}}}function wt(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function xa(){const e={get(s){return Zn(this,s)},get size(){return tr(this)},has:er,add:ds,set:hs,delete:ps,clear:ms,forEach:nr(!1,!1)},t={get(s){return Zn(this,s,!1,!0)},get size(){return tr(this)},has:er,add:ds,set:hs,delete:ps,clear:ms,forEach:nr(!1,!0)},n={get(s){return Zn(this,s,!0)},get size(){return tr(this,!0)},has(s){return er.call(this,s,!0)},add:wt("add"),set:wt("set"),delete:wt("delete"),clear:wt("clear"),forEach:nr(!0,!1)},r={get(s){return Zn(this,s,!0,!0)},get size(){return tr(this,!0)},has(s){return er.call(this,s,!0)},add:wt("add"),set:wt("set"),delete:wt("delete"),clear:wt("clear"),forEach:nr(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(s=>{e[s]=rr(s,!1,!1),n[s]=rr(s,!0,!1),t[s]=rr(s,!1,!0),r[s]=rr(s,!0,!0)}),[e,n,t,r]}const[Sa,Da,ka,za]=xa();function Do(e,t){const n=t?e?za:ka:e?Da:Sa;return(r,o,s)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(de(n,o)&&o in r?n:r,o,s)}const Va={get:Do(!1,!1)},$a={get:Do(!1,!0)},Ma={get:Do(!0,!1)},Vi=new WeakMap,$i=new WeakMap,Mi=new WeakMap,Na=new WeakMap;function Ha(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Fa(e){return e.__v_skip||!Object.isExtensible(e)?0:Ha(aa(e))}function Un(e){return pn(e)?e:ko(e,!1,Oa,Va,Vi)}function Ni(e){return ko(e,!1,Ia,$a,$i)}function Wn(e){return ko(e,!0,Ca,Ma,Mi)}function ko(e,t,n,r,o){if(!we(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const s=o.get(e);if(s)return s;const i=Fa(e);if(i===0)return e;const l=new Proxy(e,i===2?r:n);return o.set(e,l),l}function fn(e){return pn(e)?fn(e.__v_raw):!!(e&&e.__v_isReactive)}function pn(e){return!!(e&&e.__v_isReadonly)}function _r(e){return!!(e&&e.__v_isShallow)}function Hi(e){return fn(e)||pn(e)}function pe(e){const t=e&&e.__v_raw;return t?pe(t):e}function Fi(e){return mr(e,"__v_skip",!0),e}const kn=e=>we(e)?Un(e):e,zo=e=>we(e)?Wn(e):e;class Bi{constructor(t,n,r,o){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new Co(()=>t(this._value),()=>On(this,1),()=>this.dep&&xi(this.dep)),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=pe(this);return(!t._cacheable||t.effect.dirty)&&kt(t._value,t._value=t.effect.run())&&On(t,2),Vo(t),t.effect._dirtyLevel>=1&&On(t,1),t._value}set value(t){this._setter(t)}get _dirty(){return this.effect.dirty}set _dirty(t){this.effect.dirty=t}}function Ba(e,t,n=!1){let r,o;const s=le(e);return s?(r=e,o=tt):(r=e.get,o=e.set),new Bi(r,o,s||!o,n)}function Vo(e){St&&Jt&&(e=pe(e),Ci(Jt,e.dep||(e.dep=Si(()=>e.dep=void 0,e instanceof Bi?e:void 0))))}function On(e,t=2,n){e=pe(e);const r=e.dep;r&&Ii(r,t)}function je(e){return!!(e&&e.__v_isRef===!0)}function ue(e){return ji(e,!1)}function $o(e){return ji(e,!0)}function ji(e,t){return je(e)?e:new ja(e,t)}class ja{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:pe(t),this._value=n?t:kn(t)}get value(){return Vo(this),this._value}set value(t){const n=this.__v_isShallow||_r(t)||pn(t);t=n?t:pe(t),kt(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:kn(t),On(this,2))}}function te(e){return je(e)?e.value:e}const Ua={get:(e,t,n)=>te(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return je(o)&&!je(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function Ui(e){return fn(e)?e:new Proxy(e,Ua)}class Wa{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>Vo(this),()=>On(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function Ka(e){return new Wa(e)}function Mo(e){const t=ne(e)?new Array(e.length):{};for(const n in e)t[n]=Ga(e,n);return t}class qa{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return wa(pe(this._object),this._key)}}function Ga(e,t,n){const r=e[t];return je(r)?r:new qa(e,t,n)}/** +* @vue/runtime-core v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Dt(e,t,n,r){let o;try{o=r?e(...r):e()}catch(s){Kn(s,t,n)}return o}function nt(e,t,n,r){if(le(e)){const s=Dt(e,t,n,r);return s&&Ei(s)&&s.catch(i=>{Kn(i,t,n)}),s}const o=[];for(let s=0;s>>1,o=Be[r],s=Vn(o);sft&&Be.splice(t,1)}function Qa(e){ne(e)?dn.push(...e):(!Rt||!Rt.includes(e,e.allowRecurse?Kt+1:Kt))&&dn.push(e),Ki()}function vs(e,t,n=zn?ft+1:0){for(;nVn(n)-Vn(r));if(dn.length=0,Rt){Rt.push(...t);return}for(Rt=t,Kt=0;Kte.id==null?1/0:e.id,Za=(e,t)=>{const n=Vn(e)-Vn(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function qi(e){fo=!1,zn=!0,Be.sort(Za);try{for(ft=0;ftCe(m)?m.trim():m)),f&&(o=n.map(fa))}let l,a=r[l=jr(t)]||r[l=jr(rt(t))];!a&&s&&(a=r[l=jr(tn(t))]),a&&nt(a,e,6,o);const c=r[l+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,nt(c,e,6,o)}}function Gi(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const s=e.emits;let i={},l=!1;if(!le(e)){const a=c=>{const u=Gi(c,t,!0);u&&(l=!0,Se(i,u))};!n&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!s&&!l?(we(e)&&r.set(e,null),null):(ne(s)?s.forEach(a=>i[a]=null):Se(i,s),we(e)&&r.set(e,i),i)}function Sr(e,t){return!e||!Fn(t)?!1:(t=t.slice(2).replace(/Once$/,""),de(e,t[0].toLowerCase()+t.slice(1))||de(e,tn(t))||de(e,t))}let xe=null,Yi=null;function br(e){const t=xe;return xe=e,Yi=e&&e.type.__scopeId||null,t}function Me(e,t=xe,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&Os(-1);const s=br(t);let i;try{i=e(...o)}finally{br(s),r._d&&Os(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Kr(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:s,propsOptions:[i],slots:l,attrs:a,emit:c,render:u,renderCache:f,data:d,setupState:m,ctx:g,inheritAttrs:y}=e;let L,A;const C=br(e);try{if(n.shapeFlag&4){const P=o||r,F=P;L=st(u.call(F,P,f,s,m,d,g)),A=a}else{const P=t;L=st(P.length>1?P(s,{attrs:a,slots:l,emit:c}):P(s,null)),A=t.props?a:tc(a)}}catch(P){xn.length=0,Kn(P,e,1),L=oe(Qe)}let _=L;if(A&&y!==!1){const P=Object.keys(A),{shapeFlag:F}=_;P.length&&F&7&&(i&&P.some(Po)&&(A=nc(A,i)),_=Vt(_,A))}return n.dirs&&(_=Vt(_),_.dirs=_.dirs?_.dirs.concat(n.dirs):n.dirs),n.transition&&(_.transition=n.transition),L=_,br(C),L}const tc=e=>{let t;for(const n in e)(n==="class"||n==="style"||Fn(n))&&((t||(t={}))[n]=e[n]);return t},nc=(e,t)=>{const n={};for(const r in e)(!Po(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function rc(e,t,n){const{props:r,children:o,component:s}=e,{props:i,children:l,patchFlag:a}=t,c=s.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&a>=0){if(a&1024)return!0;if(a&16)return r?_s(r,i,c):!!i;if(a&8){const u=t.dynamicProps;for(let f=0;fe.__isSuspense;function Xi(e,t){t&&t.pendingBranch?ne(e)?t.effects.push(...e):t.effects.push(e):Qa(e)}const ac=Symbol.for("v-scx"),cc=()=>Pe(ac);function uc(e,t){return Ho(e,null,t)}const or={};function He(e,t,n){return Ho(e,t,n)}function Ho(e,t,{immediate:n,deep:r,flush:o,once:s,onTrack:i,onTrigger:l}=Le){if(t&&s){const H=t;t=(...M)=>{H(...M),F()}}const a=Ve,c=H=>r===!0?H:Gt(H,r===!1?1:void 0);let u,f=!1,d=!1;if(je(e)?(u=()=>e.value,f=_r(e)):fn(e)?(u=()=>c(e),f=!0):ne(e)?(d=!0,f=e.some(H=>fn(H)||_r(H)),u=()=>e.map(H=>{if(je(H))return H.value;if(fn(H))return c(H);if(le(H))return Dt(H,a,2)})):le(e)?t?u=()=>Dt(e,a,2):u=()=>(m&&m(),nt(e,a,3,[g])):u=tt,t&&r){const H=u;u=()=>Gt(H())}let m,g=H=>{m=_.onStop=()=>{Dt(H,a,4),m=_.onStop=void 0}},y;if(Yn)if(g=tt,t?n&&nt(t,a,3,[u(),d?[]:void 0,g]):u(),o==="sync"){const H=cc();y=H.__watcherHandles||(H.__watcherHandles=[])}else return tt;let L=d?new Array(e.length).fill(or):or;const A=()=>{if(!(!_.active||!_.dirty))if(t){const H=_.run();(r||f||(d?H.some((M,b)=>kt(M,L[b])):kt(H,L)))&&(m&&m(),nt(t,a,3,[H,L===or?void 0:d&&L[0]===or?[]:L,g]),L=H)}else _.run()};A.allowRecurse=!!t;let C;o==="sync"?C=A:o==="post"?C=()=>qe(A,a&&a.suspense):(A.pre=!0,a&&(A.id=a.uid),C=()=>xr(A));const _=new Co(u,tt,C),P=Pi(),F=()=>{_.stop(),P&&Ro(P.effects,_)};return t?n?A():L=_.run():o==="post"?qe(_.run.bind(_),a&&a.suspense):_.run(),y&&y.push(F),F}function fc(e,t,n){const r=this.proxy,o=Ce(e)?e.includes(".")?Qi(r,e):()=>r[e]:e.bind(r,r);let s;le(t)?s=t:(s=t.handler,n=t);const i=Gn(this),l=Ho(o,s.bind(r),n);return i(),l}function Qi(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o0){if(n>=t)return e;n++}if(r=r||new Set,r.has(e))return e;if(r.add(e),je(e))Gt(e.value,t,n,r);else if(ne(e))for(let o=0;o{Gt(o,t,n,r)});else if(Li(e))for(const o in e)Gt(e[o],t,n,r);return e}function Er(e,t){if(xe===null)return e;const n=Mr(xe)||xe.proxy,r=e.dirs||(e.dirs=[]);for(let o=0;o{e.isMounted=!0}),kr(()=>{e.isUnmounting=!0}),e}const Ze=[Function,Array],Zi={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Ze,onEnter:Ze,onAfterEnter:Ze,onEnterCancelled:Ze,onBeforeLeave:Ze,onLeave:Ze,onAfterLeave:Ze,onLeaveCancelled:Ze,onBeforeAppear:Ze,onAppear:Ze,onAfterAppear:Ze,onAppearCancelled:Ze},hc={name:"BaseTransition",props:Zi,setup(e,{slots:t}){const n=$r(),r=dc();let o;return()=>{const s=t.default&&tl(t.default(),!0);if(!s||!s.length)return;let i=s[0];if(s.length>1){for(const y of s)if(y.type!==Qe){i=y;break}}const l=pe(e),{mode:a}=l;if(r.isLeaving)return qr(i);const c=bs(i);if(!c)return qr(i);const u=ho(c,l,r,n);po(c,u);const f=n.subTree,d=f&&bs(f);let m=!1;const{getTransitionKey:g}=c.type;if(g){const y=g();o===void 0?o=y:y!==o&&(o=y,m=!0)}if(d&&d.type!==Qe&&(!qt(c,d)||m)){const y=ho(d,l,r,n);if(po(d,y),a==="out-in")return r.isLeaving=!0,y.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&(n.effect.dirty=!0,n.update())},qr(i);a==="in-out"&&c.type!==Qe&&(y.delayLeave=(L,A,C)=>{const _=el(r,d);_[String(d.key)]=d,L[Ot]=()=>{A(),L[Ot]=void 0,delete u.delayedLeave},u.delayedLeave=C})}return i}}},pc=hc;function el(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 ho(e,t,n,r){const{appear:o,mode:s,persisted:i=!1,onBeforeEnter:l,onEnter:a,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:f,onLeave:d,onAfterLeave:m,onLeaveCancelled:g,onBeforeAppear:y,onAppear:L,onAfterAppear:A,onAppearCancelled:C}=t,_=String(e.key),P=el(n,e),F=(b,k)=>{b&&nt(b,r,9,k)},H=(b,k)=>{const I=k[1];F(b,k),ne(b)?b.every(Y=>Y.length<=1)&&I():b.length<=1&&I()},M={mode:s,persisted:i,beforeEnter(b){let k=l;if(!n.isMounted)if(o)k=y||l;else return;b[Ot]&&b[Ot](!0);const I=P[_];I&&qt(e,I)&&I.el[Ot]&&I.el[Ot](),F(k,[b])},enter(b){let k=a,I=c,Y=u;if(!n.isMounted)if(o)k=L||a,I=A||c,Y=C||u;else return;let w=!1;const $=b[sr]=re=>{w||(w=!0,re?F(Y,[b]):F(I,[b]),M.delayedLeave&&M.delayedLeave(),b[sr]=void 0)};k?H(k,[b,$]):$()},leave(b,k){const I=String(e.key);if(b[sr]&&b[sr](!0),n.isUnmounting)return k();F(f,[b]);let Y=!1;const w=b[Ot]=$=>{Y||(Y=!0,k(),$?F(g,[b]):F(m,[b]),b[Ot]=void 0,P[I]===e&&delete P[I])};P[I]=e,d?H(d,[b,w]):w()},clone(b){return ho(b,t,n,r)}};return M}function qr(e){if(qn(e))return e=Vt(e),e.children=null,e}function bs(e){return qn(e)?e.children?e.children[0]:void 0:e}function po(e,t){e.shapeFlag&6&&e.component?po(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 tl(e,t=!1,n){let r=[],o=0;for(let s=0;s1)for(let s=0;s!!e.type.__asyncLoader;/*! #__NO_SIDE_EFFECTS__ */function x(e){le(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:s,suspensible:i=!0,onError:l}=e;let a=null,c,u=0;const f=()=>(u++,a=null,d()),d=()=>{let m;return a||(m=a=t().catch(g=>{if(g=g instanceof Error?g:new Error(String(g)),l)return new Promise((y,L)=>{l(g,()=>y(f()),()=>L(g),u+1)});throw g}).then(g=>m!==a&&a?a:(g&&(g.__esModule||g[Symbol.toStringTag]==="Module")&&(g=g.default),c=g,g)))};return me({name:"AsyncComponentWrapper",__asyncLoader:d,get __asyncResolved(){return c},setup(){const m=Ve;if(c)return()=>Gr(c,m);const g=C=>{a=null,Kn(C,m,13,!r)};if(i&&m.suspense||Yn)return d().then(C=>()=>Gr(C,m)).catch(C=>(g(C),()=>r?oe(r,{error:C}):null));const y=ue(!1),L=ue(),A=ue(!!o);return o&&setTimeout(()=>{A.value=!1},o),s!=null&&setTimeout(()=>{if(!y.value&&!L.value){const C=new Error(`Async component timed out after ${s}ms.`);g(C),L.value=C}},s),d().then(()=>{y.value=!0,m.parent&&qn(m.parent.vnode)&&(m.parent.effect.dirty=!0,xr(m.parent.update))}).catch(C=>{g(C),L.value=C}),()=>{if(y.value&&c)return Gr(c,m);if(L.value&&r)return oe(r,{error:L.value});if(n&&!A.value)return oe(n)}}})}function Gr(e,t){const{ref:n,props:r,children:o,ce:s}=t.vnode,i=oe(e,r,o);return i.ref=n,i.ce=s,delete t.vnode.ce,i}const qn=e=>e.type.__isKeepAlive;function mc(e,t){nl(e,"a",t)}function vc(e,t){nl(e,"da",t)}function nl(e,t,n=Ve){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(Dr(t,r,n),n){let o=n.parent;for(;o&&o.parent;)qn(o.parent.vnode)&&_c(r,t,n,o),o=o.parent}}function _c(e,t,n,r){const o=Dr(t,e,r,!0);zr(()=>{Ro(r[t],o)},n)}function Dr(e,t,n=Ve,r=!1){if(n){const o=n[e]||(n[e]=[]),s=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;nn();const l=Gn(n),a=nt(t,n,e,i);return l(),rn(),a});return r?o.unshift(s):o.push(s),s}}const Et=e=>(t,n=Ve)=>(!Yn||e==="sp")&&Dr(e,(...r)=>t(...r),n),gc=Et("bm"),We=Et("m"),bc=Et("bu"),Ec=Et("u"),kr=Et("bum"),zr=Et("um"),yc=Et("sp"),Lc=Et("rtg"),wc=Et("rtc");function Tc(e,t=Ve){Dr("ec",e,t)}function zt(e,t,n,r){let o;const s=n&&n[r];if(ne(e)||Ce(e)){o=new Array(e.length);for(let i=0,l=e.length;it(i,l,void 0,s&&s[l]));else{const i=Object.keys(e);o=new Array(i.length);for(let l=0,a=i.length;lTr(t)?!(t.type===Qe||t.type===ye&&!rl(t.children)):!0)?e:null}const mo=e=>e?vl(e)?Mr(e)||e.proxy:mo(e.parent):null,Cn=Se(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=>mo(e.parent),$root:e=>mo(e.root),$emit:e=>e.emit,$options:e=>Fo(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,xr(e.update)}),$nextTick:e=>e.n||(e.n=bn.bind(e.proxy)),$watch:e=>fc.bind(e)}),Yr=(e,t)=>e!==Le&&!e.__isScriptSetup&&de(e,t),Ac={get({_:e},t){const{ctx:n,setupState:r,data:o,props:s,accessCache:i,type:l,appContext:a}=e;let c;if(t[0]!=="$"){const m=i[t];if(m!==void 0)switch(m){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return s[t]}else{if(Yr(r,t))return i[t]=1,r[t];if(o!==Le&&de(o,t))return i[t]=2,o[t];if((c=e.propsOptions[0])&&de(c,t))return i[t]=3,s[t];if(n!==Le&&de(n,t))return i[t]=4,n[t];vo&&(i[t]=0)}}const u=Cn[t];let f,d;if(u)return t==="$attrs"&&Ye(e,"get",t),u(e);if((f=l.__cssModules)&&(f=f[t]))return f;if(n!==Le&&de(n,t))return i[t]=4,n[t];if(d=a.config.globalProperties,de(d,t))return d[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:s}=e;return Yr(o,t)?(o[t]=n,!0):r!==Le&&de(r,t)?(r[t]=n,!0):de(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(s[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:s}},i){let l;return!!n[i]||e!==Le&&de(e,i)||Yr(t,i)||(l=s[0])&&de(l,i)||de(r,i)||de(Cn,i)||de(o.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:de(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function Es(e){return ne(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let vo=!0;function Pc(e){const t=Fo(e),n=e.proxy,r=e.ctx;vo=!1,t.beforeCreate&&ys(t.beforeCreate,e,"bc");const{data:o,computed:s,methods:i,watch:l,provide:a,inject:c,created:u,beforeMount:f,mounted:d,beforeUpdate:m,updated:g,activated:y,deactivated:L,beforeDestroy:A,beforeUnmount:C,destroyed:_,unmounted:P,render:F,renderTracked:H,renderTriggered:M,errorCaptured:b,serverPrefetch:k,expose:I,inheritAttrs:Y,components:w,directives:$,filters:re}=t;if(c&&Rc(c,r,null),i)for(const X in i){const q=i[X];le(q)&&(r[X]=q.bind(n))}if(o){const X=o.call(n,n);we(X)&&(e.data=Un(X))}if(vo=!0,s)for(const X in s){const q=s[X],De=le(q)?q.bind(n,n):le(q.get)?q.get.bind(n,n):tt,$e=!le(q)&&le(q.set)?q.set.bind(n):tt,Ke=N({get:De,set:$e});Object.defineProperty(r,X,{enumerable:!0,configurable:!0,get:()=>Ke.value,set:Fe=>Ke.value=Fe})}if(l)for(const X in l)ol(l[X],r,n,X);if(a){const X=le(a)?a.call(n):a;Reflect.ownKeys(X).forEach(q=>{Qt(q,X[q])})}u&&ys(u,e,"c");function D(X,q){ne(q)?q.forEach(De=>X(De.bind(n))):q&&X(q.bind(n))}if(D(gc,f),D(We,d),D(bc,m),D(Ec,g),D(mc,y),D(vc,L),D(Tc,b),D(wc,H),D(Lc,M),D(kr,C),D(zr,P),D(yc,k),ne(I))if(I.length){const X=e.exposed||(e.exposed={});I.forEach(q=>{Object.defineProperty(X,q,{get:()=>n[q],set:De=>n[q]=De})})}else e.exposed||(e.exposed={});F&&e.render===tt&&(e.render=F),Y!=null&&(e.inheritAttrs=Y),w&&(e.components=w),$&&(e.directives=$)}function Rc(e,t,n=tt){ne(e)&&(e=_o(e));for(const r in e){const o=e[r];let s;we(o)?"default"in o?s=Pe(o.from||r,o.default,!0):s=Pe(o.from||r):s=Pe(o),je(s)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>s.value,set:i=>s.value=i}):t[r]=s}}function ys(e,t,n){nt(ne(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function ol(e,t,n,r){const o=r.includes(".")?Qi(n,r):()=>n[r];if(Ce(e)){const s=t[e];le(s)&&He(o,s)}else if(le(e))He(o,e.bind(n));else if(we(e))if(ne(e))e.forEach(s=>ol(s,t,n,r));else{const s=le(e.handler)?e.handler.bind(n):t[e.handler];le(s)&&He(o,s,e)}}function Fo(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:s,config:{optionMergeStrategies:i}}=e.appContext,l=s.get(t);let a;return l?a=l:!o.length&&!n&&!r?a=t:(a={},o.length&&o.forEach(c=>yr(a,c,i,!0)),yr(a,t,i)),we(t)&&s.set(t,a),a}function yr(e,t,n,r=!1){const{mixins:o,extends:s}=t;s&&yr(e,s,n,!0),o&&o.forEach(i=>yr(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const l=Oc[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const Oc={data:Ls,props:ws,emits:ws,methods:Pn,computed:Pn,beforeCreate:Ue,created:Ue,beforeMount:Ue,mounted:Ue,beforeUpdate:Ue,updated:Ue,beforeDestroy:Ue,beforeUnmount:Ue,destroyed:Ue,unmounted:Ue,activated:Ue,deactivated:Ue,errorCaptured:Ue,serverPrefetch:Ue,components:Pn,directives:Pn,watch:Ic,provide:Ls,inject:Cc};function Ls(e,t){return t?e?function(){return Se(le(e)?e.call(this,this):e,le(t)?t.call(this,this):t)}:t:e}function Cc(e,t){return Pn(_o(e),_o(t))}function _o(e){if(ne(e)){const t={};for(let n=0;n1)return n&&le(t)?t.call(r&&r.proxy):t}}function Dc(e,t,n,r=!1){const o={},s={};mr(s,Vr,1),e.propsDefaults=Object.create(null),il(e,t,o,s);for(const i in e.propsOptions[0])i in o||(o[i]=void 0);n?e.props=r?o:Ni(o):e.type.props?e.props=o:e.props=s,e.attrs=s}function kc(e,t,n,r){const{props:o,attrs:s,vnode:{patchFlag:i}}=e,l=pe(o),[a]=e.propsOptions;let c=!1;if((r||i>0)&&!(i&16)){if(i&8){const u=e.vnode.dynamicProps;for(let f=0;f{a=!0;const[d,m]=ll(f,t,!0);Se(i,d),m&&l.push(...m)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!s&&!a)return we(e)&&r.set(e,cn),cn;if(ne(s))for(let u=0;u-1,m[1]=y<0||g-1||de(m,"default"))&&l.push(f)}}}const c=[i,l];return we(e)&&r.set(e,c),c}function Ts(e){return e[0]!=="$"}function As(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function Ps(e,t){return As(e)===As(t)}function Rs(e,t){return ne(t)?t.findIndex(n=>Ps(n,e)):le(t)&&Ps(t,e)?0:-1}const al=e=>e[0]==="_"||e==="$stable",Bo=e=>ne(e)?e.map(st):[st(e)],zc=(e,t,n)=>{if(t._n)return t;const r=Me((...o)=>Bo(t(...o)),n);return r._c=!1,r},cl=(e,t,n)=>{const r=e._ctx;for(const o in e){if(al(o))continue;const s=e[o];if(le(s))t[o]=zc(o,s,r);else if(s!=null){const i=Bo(s);t[o]=()=>i}}},ul=(e,t)=>{const n=Bo(t);e.slots.default=()=>n},Vc=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=pe(t),mr(t,"_",n)):cl(t,e.slots={})}else e.slots={},t&&ul(e,t);mr(e.slots,Vr,1)},$c=(e,t,n)=>{const{vnode:r,slots:o}=e;let s=!0,i=Le;if(r.shapeFlag&32){const l=t._;l?n&&l===1?s=!1:(Se(o,t),!n&&l===1&&delete o._):(s=!t.$stable,cl(t,o)),i=t}else t&&(ul(e,t),i={default:1});if(s)for(const l in o)!al(l)&&i[l]==null&&delete o[l]};function wr(e,t,n,r,o=!1){if(ne(e)){e.forEach((d,m)=>wr(d,t&&(ne(t)?t[m]:t),n,r,o));return}if(hn(r)&&!o)return;const s=r.shapeFlag&4?Mr(r.component)||r.component.proxy:r.el,i=o?null:s,{i:l,r:a}=e,c=t&&t.r,u=l.refs===Le?l.refs={}:l.refs,f=l.setupState;if(c!=null&&c!==a&&(Ce(c)?(u[c]=null,de(f,c)&&(f[c]=null)):je(c)&&(c.value=null)),le(a))Dt(a,l,12,[i,u]);else{const d=Ce(a),m=je(a),g=e.f;if(d||m){const y=()=>{if(g){const L=d?de(f,a)?f[a]:u[a]:a.value;o?ne(L)&&Ro(L,s):ne(L)?L.includes(s)||L.push(s):d?(u[a]=[s],de(f,a)&&(f[a]=u[a])):(a.value=[s],e.k&&(u[e.k]=a.value))}else d?(u[a]=i,de(f,a)&&(f[a]=i)):m&&(a.value=i,e.k&&(u[e.k]=i))};o||g?y():(y.id=-1,qe(y,n))}}}let Tt=!1;const Mc=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",Nc=e=>e.namespaceURI.includes("MathML"),ir=e=>{if(Mc(e))return"svg";if(Nc(e))return"mathml"},lr=e=>e.nodeType===8;function Hc(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:s,parentNode:i,remove:l,insert:a,createComment:c}}=e,u=(_,P)=>{if(!P.hasChildNodes()){n(null,_,P),gr(),P._vnode=_;return}Tt=!1,f(P.firstChild,_,null,null,null),gr(),P._vnode=_,Tt&&console.error("Hydration completed but contains mismatches.")},f=(_,P,F,H,M,b=!1)=>{const k=lr(_)&&_.data==="[",I=()=>y(_,P,F,H,M,k),{type:Y,ref:w,shapeFlag:$,patchFlag:re}=P;let ie=_.nodeType;P.el=_,re===-2&&(b=!1,P.dynamicChildren=null);let D=null;switch(Y){case mn:ie!==3?P.children===""?(a(P.el=o(""),i(_),_),D=_):D=I():(_.data!==P.children&&(Tt=!0,_.data=P.children),D=s(_));break;case Qe:C(_)?(D=s(_),A(P.el=_.content.firstChild,_,F)):ie!==8||k?D=I():D=s(_);break;case In:if(k&&(_=s(_),ie=_.nodeType),ie===1||ie===3){D=_;const X=!P.children.length;for(let q=0;q{b=b||!!P.dynamicChildren;const{type:k,props:I,patchFlag:Y,shapeFlag:w,dirs:$,transition:re}=P,ie=k==="input"||k==="option";if(ie||Y!==-1){$&&ut(P,null,F,"created");let D=!1;if(C(_)){D=fl(H,re)&&F&&F.vnode.props&&F.vnode.props.appear;const q=_.content.firstChild;D&&re.beforeEnter(q),A(q,_,F),P.el=_=q}if(w&16&&!(I&&(I.innerHTML||I.textContent))){let q=m(_.firstChild,P,_,F,H,M,b);for(;q;){Tt=!0;const De=q;q=q.nextSibling,l(De)}}else w&8&&_.textContent!==P.children&&(Tt=!0,_.textContent=P.children);if(I)if(ie||!b||Y&48)for(const q in I)(ie&&(q.endsWith("value")||q==="indeterminate")||Fn(q)&&!Rn(q)||q[0]===".")&&r(_,q,null,I[q],void 0,void 0,F);else I.onClick&&r(_,"onClick",null,I.onClick,void 0,void 0,F);let X;(X=I&&I.onVnodeBeforeMount)&&et(X,F,P),$&&ut(P,null,F,"beforeMount"),((X=I&&I.onVnodeMounted)||$||D)&&Xi(()=>{X&&et(X,F,P),D&&re.enter(_),$&&ut(P,null,F,"mounted")},H)}return _.nextSibling},m=(_,P,F,H,M,b,k)=>{k=k||!!P.dynamicChildren;const I=P.children,Y=I.length;for(let w=0;w{const{slotScopeIds:k}=P;k&&(M=M?M.concat(k):k);const I=i(_),Y=m(s(_),P,I,F,H,M,b);return Y&&lr(Y)&&Y.data==="]"?s(P.anchor=Y):(Tt=!0,a(P.anchor=c("]"),I,Y),Y)},y=(_,P,F,H,M,b)=>{if(Tt=!0,P.el=null,b){const Y=L(_);for(;;){const w=s(_);if(w&&w!==Y)l(w);else break}}const k=s(_),I=i(_);return l(_),n(null,P,I,k,F,H,ir(I),M),k},L=(_,P="[",F="]")=>{let H=0;for(;_;)if(_=s(_),_&&lr(_)&&(_.data===P&&H++,_.data===F)){if(H===0)return s(_);H--}return _},A=(_,P,F)=>{const H=P.parentNode;H&&H.replaceChild(_,P);let M=F;for(;M;)M.vnode.el===P&&(M.vnode.el=M.subTree.el=_),M=M.parent},C=_=>_.nodeType===1&&_.tagName.toLowerCase()==="template";return[u,f]}const qe=Xi;function Fc(e){return Bc(e,Hc)}function Bc(e,t){const n=wi();n.__VUE__=!0;const{insert:r,remove:o,patchProp:s,createElement:i,createText:l,createComment:a,setText:c,setElementText:u,parentNode:f,nextSibling:d,setScopeId:m=tt,insertStaticContent:g}=e,y=(h,p,E,O=null,T=null,z=null,U=void 0,V=null,B=!!p.dynamicChildren)=>{if(h===p)return;h&&!qt(h,p)&&(O=R(h),Fe(h,T,z,!0),h=null),p.patchFlag===-2&&(B=!1,p.dynamicChildren=null);const{type:S,ref:G,shapeFlag:ee}=p;switch(S){case mn:L(h,p,E,O);break;case Qe:A(h,p,E,O);break;case In:h==null&&C(p,E,O,U);break;case ye:w(h,p,E,O,T,z,U,V,B);break;default:ee&1?F(h,p,E,O,T,z,U,V,B):ee&6?$(h,p,E,O,T,z,U,V,B):(ee&64||ee&128)&&S.process(h,p,E,O,T,z,U,V,B,J)}G!=null&&T&&wr(G,h&&h.ref,z,p||h,!p)},L=(h,p,E,O)=>{if(h==null)r(p.el=l(p.children),E,O);else{const T=p.el=h.el;p.children!==h.children&&c(T,p.children)}},A=(h,p,E,O)=>{h==null?r(p.el=a(p.children||""),E,O):p.el=h.el},C=(h,p,E,O)=>{[h.el,h.anchor]=g(h.children,p,E,O,h.el,h.anchor)},_=({el:h,anchor:p},E,O)=>{let T;for(;h&&h!==p;)T=d(h),r(h,E,O),h=T;r(p,E,O)},P=({el:h,anchor:p})=>{let E;for(;h&&h!==p;)E=d(h),o(h),h=E;o(p)},F=(h,p,E,O,T,z,U,V,B)=>{p.type==="svg"?U="svg":p.type==="math"&&(U="mathml"),h==null?H(p,E,O,T,z,U,V,B):k(h,p,T,z,U,V,B)},H=(h,p,E,O,T,z,U,V)=>{let B,S;const{props:G,shapeFlag:ee,transition:Q,dirs:se}=h;if(B=h.el=i(h.type,z,G&&G.is,G),ee&8?u(B,h.children):ee&16&&b(h.children,B,null,O,T,Jr(h,z),U,V),se&&ut(h,null,O,"created"),M(B,h,h.scopeId,U,O),G){for(const be in G)be!=="value"&&!Rn(be)&&s(B,be,null,G[be],z,h.children,O,T,ke);"value"in G&&s(B,"value",null,G.value,z),(S=G.onVnodeBeforeMount)&&et(S,O,h)}se&&ut(h,null,O,"beforeMount");const ae=fl(T,Q);ae&&Q.beforeEnter(B),r(B,p,E),((S=G&&G.onVnodeMounted)||ae||se)&&qe(()=>{S&&et(S,O,h),ae&&Q.enter(B),se&&ut(h,null,O,"mounted")},T)},M=(h,p,E,O,T)=>{if(E&&m(h,E),O)for(let z=0;z{for(let S=B;S{const V=p.el=h.el;let{patchFlag:B,dynamicChildren:S,dirs:G}=p;B|=h.patchFlag&16;const ee=h.props||Le,Q=p.props||Le;let se;if(E&&Bt(E,!1),(se=Q.onVnodeBeforeUpdate)&&et(se,E,p,h),G&&ut(p,h,E,"beforeUpdate"),E&&Bt(E,!0),S?I(h.dynamicChildren,S,V,E,O,Jr(p,T),z):U||q(h,p,V,null,E,O,Jr(p,T),z,!1),B>0){if(B&16)Y(V,p,ee,Q,E,O,T);else if(B&2&&ee.class!==Q.class&&s(V,"class",null,Q.class,T),B&4&&s(V,"style",ee.style,Q.style,T),B&8){const ae=p.dynamicProps;for(let be=0;be{se&&et(se,E,p,h),G&&ut(p,h,E,"updated")},O)},I=(h,p,E,O,T,z,U)=>{for(let V=0;V{if(E!==O){if(E!==Le)for(const V in E)!Rn(V)&&!(V in O)&&s(h,V,E[V],null,U,p.children,T,z,ke);for(const V in O){if(Rn(V))continue;const B=O[V],S=E[V];B!==S&&V!=="value"&&s(h,V,S,B,U,p.children,T,z,ke)}"value"in O&&s(h,"value",E.value,O.value,U)}},w=(h,p,E,O,T,z,U,V,B)=>{const S=p.el=h?h.el:l(""),G=p.anchor=h?h.anchor:l("");let{patchFlag:ee,dynamicChildren:Q,slotScopeIds:se}=p;se&&(V=V?V.concat(se):se),h==null?(r(S,E,O),r(G,E,O),b(p.children||[],E,G,T,z,U,V,B)):ee>0&&ee&64&&Q&&h.dynamicChildren?(I(h.dynamicChildren,Q,E,T,z,U,V),(p.key!=null||T&&p===T.subTree)&&dl(h,p,!0)):q(h,p,E,G,T,z,U,V,B)},$=(h,p,E,O,T,z,U,V,B)=>{p.slotScopeIds=V,h==null?p.shapeFlag&512?T.ctx.activate(p,E,O,U,B):re(p,E,O,T,z,U,B):ie(h,p,B)},re=(h,p,E,O,T,z,U)=>{const V=h.component=Xc(h,O,T);if(qn(h)&&(V.ctx.renderer=J),Qc(V),V.asyncDep){if(T&&T.registerDep(V,D),!h.el){const B=V.subTree=oe(Qe);A(null,B,p,E)}}else D(V,h,p,E,T,z,U)},ie=(h,p,E)=>{const O=p.component=h.component;if(rc(h,p,E))if(O.asyncDep&&!O.asyncResolved){X(O,p,E);return}else O.next=p,Xa(O.update),O.effect.dirty=!0,O.update();else p.el=h.el,O.vnode=p},D=(h,p,E,O,T,z,U)=>{const V=()=>{if(h.isMounted){let{next:G,bu:ee,u:Q,parent:se,vnode:ae}=h;{const on=hl(h);if(on){G&&(G.el=ae.el,X(h,G,U)),on.asyncDep.then(()=>{h.isUnmounted||V()});return}}let be=G,Te;Bt(h,!1),G?(G.el=ae.el,X(h,G,U)):G=ae,ee&&Ur(ee),(Te=G.props&&G.props.onVnodeBeforeUpdate)&&et(Te,se,G,ae),Bt(h,!0);const ze=Kr(h),ot=h.subTree;h.subTree=ze,y(ot,ze,f(ot.el),R(ot),h,T,z),G.el=ze.el,be===null&&oc(h,ze.el),Q&&qe(Q,T),(Te=G.props&&G.props.onVnodeUpdated)&&qe(()=>et(Te,se,G,ae),T)}else{let G;const{el:ee,props:Q}=p,{bm:se,m:ae,parent:be}=h,Te=hn(p);if(Bt(h,!1),se&&Ur(se),!Te&&(G=Q&&Q.onVnodeBeforeMount)&&et(G,be,p),Bt(h,!0),ee&&ge){const ze=()=>{h.subTree=Kr(h),ge(ee,h.subTree,h,T,null)};Te?p.type.__asyncLoader().then(()=>!h.isUnmounted&&ze()):ze()}else{const ze=h.subTree=Kr(h);y(null,ze,E,O,h,T,z),p.el=ze.el}if(ae&&qe(ae,T),!Te&&(G=Q&&Q.onVnodeMounted)){const ze=p;qe(()=>et(G,be,ze),T)}(p.shapeFlag&256||be&&hn(be.vnode)&&be.vnode.shapeFlag&256)&&h.a&&qe(h.a,T),h.isMounted=!0,p=E=O=null}},B=h.effect=new Co(V,tt,()=>xr(S),h.scope),S=h.update=()=>{B.dirty&&B.run()};S.id=h.uid,Bt(h,!0),S()},X=(h,p,E)=>{p.component=h;const O=h.vnode.props;h.vnode=p,h.next=null,kc(h,p.props,O,E),$c(h,p.children,E),nn(),vs(h),rn()},q=(h,p,E,O,T,z,U,V,B=!1)=>{const S=h&&h.children,G=h?h.shapeFlag:0,ee=p.children,{patchFlag:Q,shapeFlag:se}=p;if(Q>0){if(Q&128){$e(S,ee,E,O,T,z,U,V,B);return}else if(Q&256){De(S,ee,E,O,T,z,U,V,B);return}}se&8?(G&16&&ke(S,T,z),ee!==S&&u(E,ee)):G&16?se&16?$e(S,ee,E,O,T,z,U,V,B):ke(S,T,z,!0):(G&8&&u(E,""),se&16&&b(ee,E,O,T,z,U,V,B))},De=(h,p,E,O,T,z,U,V,B)=>{h=h||cn,p=p||cn;const S=h.length,G=p.length,ee=Math.min(S,G);let Q;for(Q=0;QG?ke(h,T,z,!0,!1,ee):b(p,E,O,T,z,U,V,B,ee)},$e=(h,p,E,O,T,z,U,V,B)=>{let S=0;const G=p.length;let ee=h.length-1,Q=G-1;for(;S<=ee&&S<=Q;){const se=h[S],ae=p[S]=B?Ct(p[S]):st(p[S]);if(qt(se,ae))y(se,ae,E,null,T,z,U,V,B);else break;S++}for(;S<=ee&&S<=Q;){const se=h[ee],ae=p[Q]=B?Ct(p[Q]):st(p[Q]);if(qt(se,ae))y(se,ae,E,null,T,z,U,V,B);else break;ee--,Q--}if(S>ee){if(S<=Q){const se=Q+1,ae=seQ)for(;S<=ee;)Fe(h[S],T,z,!0),S++;else{const se=S,ae=S,be=new Map;for(S=ae;S<=Q;S++){const Je=p[S]=B?Ct(p[S]):st(p[S]);Je.key!=null&&be.set(Je.key,S)}let Te,ze=0;const ot=Q-ae+1;let on=!1,ss=0;const yn=new Array(ot);for(S=0;S=ot){Fe(Je,T,z,!0);continue}let ct;if(Je.key!=null)ct=be.get(Je.key);else for(Te=ae;Te<=Q;Te++)if(yn[Te-ae]===0&&qt(Je,p[Te])){ct=Te;break}ct===void 0?Fe(Je,T,z,!0):(yn[ct-ae]=S+1,ct>=ss?ss=ct:on=!0,y(Je,p[ct],E,null,T,z,U,V,B),ze++)}const is=on?jc(yn):cn;for(Te=is.length-1,S=ot-1;S>=0;S--){const Je=ae+S,ct=p[Je],ls=Je+1{const{el:z,type:U,transition:V,children:B,shapeFlag:S}=h;if(S&6){Ke(h.component.subTree,p,E,O);return}if(S&128){h.suspense.move(p,E,O);return}if(S&64){U.move(h,p,E,J);return}if(U===ye){r(z,p,E);for(let ee=0;eeV.enter(z),T);else{const{leave:ee,delayLeave:Q,afterLeave:se}=V,ae=()=>r(z,p,E),be=()=>{ee(z,()=>{ae(),se&&se()})};Q?Q(z,ae,be):be()}else r(z,p,E)},Fe=(h,p,E,O=!1,T=!1)=>{const{type:z,props:U,ref:V,children:B,dynamicChildren:S,shapeFlag:G,patchFlag:ee,dirs:Q}=h;if(V!=null&&wr(V,null,E,h,!0),G&256){p.ctx.deactivate(h);return}const se=G&1&&Q,ae=!hn(h);let be;if(ae&&(be=U&&U.onVnodeBeforeUnmount)&&et(be,p,h),G&6)at(h.component,E,O);else{if(G&128){h.suspense.unmount(E,O);return}se&&ut(h,null,p,"beforeUnmount"),G&64?h.type.remove(h,p,E,T,J,O):S&&(z!==ye||ee>0&&ee&64)?ke(S,p,E,!1,!0):(z===ye&&ee&384||!T&&G&16)&&ke(B,p,E),O&&yt(h)}(ae&&(be=U&&U.onVnodeUnmounted)||se)&&qe(()=>{be&&et(be,p,h),se&&ut(h,null,p,"unmounted")},E)},yt=h=>{const{type:p,el:E,anchor:O,transition:T}=h;if(p===ye){Lt(E,O);return}if(p===In){P(h);return}const z=()=>{o(E),T&&!T.persisted&&T.afterLeave&&T.afterLeave()};if(h.shapeFlag&1&&T&&!T.persisted){const{leave:U,delayLeave:V}=T,B=()=>U(E,z);V?V(h.el,z,B):B()}else z()},Lt=(h,p)=>{let E;for(;h!==p;)E=d(h),o(h),h=E;o(p)},at=(h,p,E)=>{const{bum:O,scope:T,update:z,subTree:U,um:V}=h;O&&Ur(O),T.stop(),z&&(z.active=!1,Fe(U,h,p,E)),V&&qe(V,p),qe(()=>{h.isUnmounted=!0},p),p&&p.pendingBranch&&!p.isUnmounted&&h.asyncDep&&!h.asyncResolved&&h.suspenseId===p.pendingId&&(p.deps--,p.deps===0&&p.resolve())},ke=(h,p,E,O=!1,T=!1,z=0)=>{for(let U=z;Uh.shapeFlag&6?R(h.component.subTree):h.shapeFlag&128?h.suspense.next():d(h.anchor||h.el);let K=!1;const j=(h,p,E)=>{h==null?p._vnode&&Fe(p._vnode,null,null,!0):y(p._vnode||null,h,p,null,null,null,E),K||(K=!0,vs(),gr(),K=!1),p._vnode=h},J={p:y,um:Fe,m:Ke,r:yt,mt:re,mc:b,pc:q,pbc:I,n:R,o:e};let ce,ge;return t&&([ce,ge]=t(J)),{render:j,hydrate:ce,createApp:Sc(j,ce)}}function Jr({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 Bt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function fl(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function dl(e,t,n=!1){const r=e.children,o=t.children;if(ne(r)&&ne(o))for(let s=0;s>1,e[n[l]]0&&(t[r]=n[s-1]),n[s]=r)}}for(s=n.length,i=n[s-1];s-- >0;)n[s]=i,i=t[i];return n}function hl(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:hl(t)}const Uc=e=>e.__isTeleport,ye=Symbol.for("v-fgt"),mn=Symbol.for("v-txt"),Qe=Symbol.for("v-cmt"),In=Symbol.for("v-stc"),xn=[];let it=null;function W(e=!1){xn.push(it=e?null:[])}function Wc(){xn.pop(),it=xn[xn.length-1]||null}let $n=1;function Os(e){$n+=e}function pl(e){return e.dynamicChildren=$n>0?it||cn:null,Wc(),$n>0&&it&&it.push(e),e}function Z(e,t,n,r,o,s){return pl(he(e,t,n,r,o,s,!0))}function Re(e,t,n,r,o){return pl(oe(e,t,n,r,o,!0))}function Tr(e){return e?e.__v_isVNode===!0:!1}function qt(e,t){return e.type===t.type&&e.key===t.key}const Vr="__vInternal",ml=({key:e})=>e??null,hr=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?Ce(e)||je(e)||le(e)?{i:xe,r:e,k:t,f:!!n}:e:null);function he(e,t=null,n=null,r=0,o=null,s=e===ye?0:1,i=!1,l=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ml(t),ref:t&&hr(t),scopeId:Yi,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:s,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:xe};return l?(jo(a,n),s&128&&e.normalize(a)):n&&(a.shapeFlag|=Ce(n)?8:16),$n>0&&!i&&it&&(a.patchFlag>0||s&6)&&a.patchFlag!==32&&it.push(a),a}const oe=Kc;function Kc(e,t=null,n=null,r=0,o=null,s=!1){if((!e||e===sc)&&(e=Qe),Tr(e)){const l=Vt(e,t,!0);return n&&jo(l,n),$n>0&&!s&&it&&(l.shapeFlag&6?it[it.indexOf(e)]=l:it.push(l)),l.patchFlag|=-2,l}if(ru(e)&&(e=e.__vccOpts),t){t=qc(t);let{class:l,style:a}=t;l&&!Ce(l)&&(t.class=Ge(l)),we(a)&&(Hi(a)&&!ne(a)&&(a=Se({},a)),t.style=jn(a))}const i=Ce(e)?1:lc(e)?128:Uc(e)?64:we(e)?4:le(e)?2:0;return he(e,t,n,r,o,i,s,!0)}function qc(e){return e?Hi(e)||Vr in e?Se({},e):e:null}function Vt(e,t,n=!1){const{props:r,ref:o,patchFlag:s,children:i}=e,l=t?bo(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&ml(l),ref:t&&t.ref?n&&o?ne(o)?o.concat(hr(t)):[o,hr(t)]:hr(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,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:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Vt(e.ssContent),ssFallback:e.ssFallback&&Vt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function $t(e=" ",t=0){return oe(mn,null,e,t)}function Gc(e,t){const n=oe(In,null,e);return n.staticCount=t,n}function Oe(e="",t=!1){return t?(W(),Re(Qe,null,e)):oe(Qe,null,e)}function st(e){return e==null||typeof e=="boolean"?oe(Qe):ne(e)?oe(ye,null,e.slice()):typeof e=="object"?Ct(e):oe(mn,null,String(e))}function Ct(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Vt(e)}function jo(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(ne(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),jo(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;!o&&!(Vr in t)?t._ctx=xe:o===3&&xe&&(xe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else le(t)?(t={default:t,_ctx:xe},n=32):(t=String(t),r&64?(n=16,t=[$t(t)]):n=8);e.children=t,e.shapeFlag|=n}function bo(...e){const t={};for(let n=0;nVe||xe;let Ar,Eo;{const e=wi(),t=(n,r)=>{let o;return(o=e[n])||(o=e[n]=[]),o.push(r),s=>{o.length>1?o.forEach(i=>i(s)):o[0](s)}};Ar=t("__VUE_INSTANCE_SETTERS__",n=>Ve=n),Eo=t("__VUE_SSR_SETTERS__",n=>Yn=n)}const Gn=e=>{const t=Ve;return Ar(e),e.scope.on(),()=>{e.scope.off(),Ar(t)}},Cs=()=>{Ve&&Ve.scope.off(),Ar(null)};function vl(e){return e.vnode.shapeFlag&4}let Yn=!1;function Qc(e,t=!1){t&&Eo(t);const{props:n,children:r}=e.vnode,o=vl(e);Dc(e,n,o,t),Vc(e,r);const s=o?Zc(e,t):void 0;return t&&Eo(!1),s}function Zc(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Fi(new Proxy(e.ctx,Ac));const{setup:r}=n;if(r){const o=e.setupContext=r.length>1?tu(e):null,s=Gn(e);nn();const i=Dt(r,e,0,[e.props,o]);if(rn(),s(),Ei(i)){if(i.then(Cs,Cs),t)return i.then(l=>{Is(e,l,t)}).catch(l=>{Kn(l,e,0)});e.asyncDep=i}else Is(e,i,t)}else _l(e,t)}function Is(e,t,n){le(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:we(t)&&(e.setupState=Ui(t)),_l(e,n)}let xs;function _l(e,t,n){const r=e.type;if(!e.render){if(!t&&xs&&!r.render){const o=r.template||Fo(e).template;if(o){const{isCustomElement:s,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:a}=r,c=Se(Se({isCustomElement:s,delimiters:l},i),a);r.render=xs(o,c)}}e.render=r.render||tt}{const o=Gn(e);nn();try{Pc(e)}finally{rn(),o()}}}function eu(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return Ye(e,"get","$attrs"),t[n]}}))}function tu(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return eu(e)},slots:e.slots,emit:e.emit,expose:t}}function Mr(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Ui(Fi(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Cn)return Cn[n](e)},has(t,n){return n in t||n in Cn}}))}function nu(e,t=!0){return le(e)?e.displayName||e.name:e.name||t&&e.__name}function ru(e){return le(e)&&"__vccOpts"in e}const N=(e,t)=>Ba(e,t,Yn);function _e(e,t,n){const r=arguments.length;return r===2?we(t)&&!ne(t)?Tr(t)?oe(e,null,[t]):oe(e,t):oe(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Tr(n)&&(n=[n]),oe(e,t,n))}const ou="3.4.15";/** +* @vue/runtime-dom v3.4.15 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/const su="http://www.w3.org/2000/svg",iu="http://www.w3.org/1998/Math/MathML",It=typeof document<"u"?document:null,Ss=It&&It.createElement("template"),lu={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"?It.createElementNS(su,e):t==="mathml"?It.createElementNS(iu,e):It.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>It.createTextNode(e),createComment:e=>It.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>It.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,s){const i=n?n.previousSibling:t.lastChild;if(o&&(o===s||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===s||!(o=o.nextSibling)););else{Ss.innerHTML=r==="svg"?`${e}`:r==="mathml"?`${e}`:e;const l=Ss.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[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},At="transition",Ln="animation",Mn=Symbol("_vtc"),Jn=(e,{slots:t})=>_e(pc,au(e),t);Jn.displayName="Transition";const gl={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};Jn.props=Se({},Zi,gl);const jt=(e,t=[])=>{ne(e)?e.forEach(n=>n(...t)):e&&e(...t)},Ds=e=>e?ne(e)?e.some(t=>t.length>1):e.length>1:!1;function au(e){const t={};for(const w in e)w in gl||(t[w]=e[w]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:s=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:a=s,appearActiveClass:c=i,appearToClass:u=l,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:d=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=e,g=cu(o),y=g&&g[0],L=g&&g[1],{onBeforeEnter:A,onEnter:C,onEnterCancelled:_,onLeave:P,onLeaveCancelled:F,onBeforeAppear:H=A,onAppear:M=C,onAppearCancelled:b=_}=t,k=(w,$,re)=>{Ut(w,$?u:l),Ut(w,$?c:i),re&&re()},I=(w,$)=>{w._isLeaving=!1,Ut(w,f),Ut(w,m),Ut(w,d),$&&$()},Y=w=>($,re)=>{const ie=w?M:C,D=()=>k($,w,re);jt(ie,[$,D]),ks(()=>{Ut($,w?a:s),Pt($,w?u:l),Ds(ie)||zs($,r,y,D)})};return Se(t,{onBeforeEnter(w){jt(A,[w]),Pt(w,s),Pt(w,i)},onBeforeAppear(w){jt(H,[w]),Pt(w,a),Pt(w,c)},onEnter:Y(!1),onAppear:Y(!0),onLeave(w,$){w._isLeaving=!0;const re=()=>I(w,$);Pt(w,f),du(),Pt(w,d),ks(()=>{w._isLeaving&&(Ut(w,f),Pt(w,m),Ds(P)||zs(w,r,L,re))}),jt(P,[w,re])},onEnterCancelled(w){k(w,!1),jt(_,[w])},onAppearCancelled(w){k(w,!0),jt(b,[w])},onLeaveCancelled(w){I(w),jt(F,[w])}})}function cu(e){if(e==null)return null;if(we(e))return[Xr(e.enter),Xr(e.leave)];{const t=Xr(e);return[t,t]}}function Xr(e){return da(e)}function Pt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[Mn]||(e[Mn]=new Set)).add(t)}function Ut(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const n=e[Mn];n&&(n.delete(t),n.size||(e[Mn]=void 0))}function ks(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let uu=0;function zs(e,t,n,r){const o=e._endId=++uu,s=()=>{o===e._endId&&r()};if(n)return setTimeout(s,n);const{type:i,timeout:l,propCount:a}=fu(e,t);if(!i)return r();const c=i+"end";let u=0;const f=()=>{e.removeEventListener(c,d),s()},d=m=>{m.target===e&&++u>=a&&f()};setTimeout(()=>{u(n[g]||"").split(", "),o=r(`${At}Delay`),s=r(`${At}Duration`),i=Vs(o,s),l=r(`${Ln}Delay`),a=r(`${Ln}Duration`),c=Vs(l,a);let u=null,f=0,d=0;t===At?i>0&&(u=At,f=i,d=s.length):t===Ln?c>0&&(u=Ln,f=c,d=a.length):(f=Math.max(i,c),u=f>0?i>c?At:Ln:null,d=u?u===At?s.length:a.length:0);const m=u===At&&/\b(transform|all)(,|$)/.test(r(`${At}Property`).toString());return{type:u,timeout:f,propCount:d,hasTransform:m}}function Vs(e,t){for(;e.length$s(n)+$s(e[r])))}function $s(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function du(){return document.body.offsetHeight}function hu(e,t,n){const r=e[Mn];r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Uo=Symbol("_vod"),Pr={beforeMount(e,{value:t},{transition:n}){e[Uo]=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):wn(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),wn(e,!0),r.enter(e)):r.leave(e,()=>{wn(e,!1)}):wn(e,t))},beforeUnmount(e,{value:t}){wn(e,t)}};function wn(e,t){e.style.display=t?e[Uo]:"none"}const pu=Symbol("");function mu(e,t,n){const r=e.style,o=r.display,s=Ce(n);if(n&&!s){if(t&&!Ce(t))for(const i in t)n[i]==null&&yo(r,i,"");for(const i in n)yo(r,i,n[i])}else if(s){if(t!==n){const i=r[pu];i&&(n+=";"+i),r.cssText=n}}else t&&e.removeAttribute("style");Uo in e&&(r.display=o)}const Ms=/\s*!important$/;function yo(e,t,n){if(ne(n))n.forEach(r=>yo(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=vu(e,t);Ms.test(n)?e.setProperty(tn(r),n.replace(Ms,""),"important"):e[r]=n}}const Ns=["Webkit","Moz","ms"],Qr={};function vu(e,t){const n=Qr[t];if(n)return n;let r=rt(t);if(r!=="filter"&&r in e)return Qr[t]=r;r=Bn(r);for(let o=0;oZr||(wu.then(()=>Zr=0),Zr=Date.now());function Au(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;nt(Pu(r,n.value),t,5,[r])};return n.value=e,n.attached=Tu(),n}function Pu(e,t){if(ne(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 js=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Ru=(e,t,n,r,o,s,i,l,a)=>{const c=o==="svg";t==="class"?hu(e,r,c):t==="style"?mu(e,n,r):Fn(t)?Po(t)||yu(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Ou(e,t,r,c))?gu(e,t,r,s,i,l,a):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),_u(e,t,r,c))};function Ou(e,t,n,r){if(r)return!!(t==="innerHTML"||t==="textContent"||t in e&&js(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 js(t)&&Ce(n)?!1:t in e}const Cu={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},Iu=(e,t)=>{const n=e._withKeys||(e._withKeys={}),r=t.join(".");return n[r]||(n[r]=o=>{if(!("key"in o))return;const s=tn(o.key);if(t.some(i=>i===s||Cu[i]===s))return e(o)})},xu=Se({patchProp:Ru},lu);let eo,Us=!1;function Su(){return eo=Us?eo:Fc(xu),Us=!0,eo}const Du=(...e)=>{const t=Su().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=zu(r);if(o)return n(o,!0,ku(o))},t};function ku(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function zu(e){return Ce(e)?document.querySelector(e):e}const Vu="modulepreload",$u=function(e){return"/ecosystem/"+e},Ws={},v=function(t,n,r){let o=Promise.resolve();if(n&&n.length>0){const s=document.getElementsByTagName("link");o=Promise.all(n.map(i=>{if(i=$u(i),i in Ws)return;Ws[i]=!0;const l=i.endsWith(".css"),a=l?'[rel="stylesheet"]':"";if(!!r)for(let f=s.length-1;f>=0;f--){const d=s[f];if(d.href===i&&(!l||d.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${a}`))return;const u=document.createElement("link");if(u.rel=l?"stylesheet":Vu,l||(u.as="script",u.crossOrigin=""),u.href=i,document.head.appendChild(u),l)return new Promise((f,d)=>{u.addEventListener("load",f),u.addEventListener("error",()=>d(new Error(`Unable to preload CSS for ${i}`)))})}))}return o.then(()=>t()).catch(s=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s})},Mu={"v-8daa1a0e":()=>v(()=>import("./index.html-0MkuVl9g.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-b9e358fe":()=>v(()=>import("./active-header-links.html-JVsX-akm.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-2f74e658":()=>v(()=>import("./back-to-top.html-J5Qq7R-Y.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3f3ff580":()=>v(()=>import("./container.html-MvKccPWY.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3b611b3c":()=>v(()=>import("./copy-code.html-XwXroN--.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3aef69fa":()=>v(()=>import("./copyright.html-cTO_2UMM.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-e97df6fe":()=>v(()=>import("./docsearch.html-kMZ0w7Tn.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-1dcbe4a7":()=>v(()=>import("./external-link-icon.html-And8gISn.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-561922cf":()=>v(()=>import("./git.html-72XjuwoH.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-190944c2":()=>v(()=>import("./google-analytics.html-U0NbYbPH.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-145379d4":()=>v(()=>import("./medium-zoom.html-nUp5JSFy.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-481bcda6":()=>v(()=>import("./nprogress.html-Sat3hoSx.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-304d59c6":()=>v(()=>import("./palette.html-IIBOm8e9.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-fbc77552":()=>v(()=>import("./prismjs.html-DIgNkqCq.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-60cd04a8":()=>v(()=>import("./pwa-popup.html-vgfvPd3T.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-7da23f32":()=>v(()=>import("./pwa.html-Brwoz8J_.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-6553daba":()=>v(()=>import("./reading-time.html-r9J-dZBq.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-6a3f6e16":()=>v(()=>import("./redirect.html-CCGsuo31.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3203da3e":()=>v(()=>import("./register-components.html-H1Om_p-O.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-cfa3cd00":()=>v(()=>import("./remove-pwa.html-ptxYFHYI.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-0503d737":()=>v(()=>import("./rtl.html-5KfB6Rkt.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-12124eae":()=>v(()=>import("./search.html-gMcjE234.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-35900b8f":()=>v(()=>import("./shiki.html-b2u78EYO.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-4091b4e3":()=>v(()=>import("./theme-data.html-6IZWf1rZ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-862929ce":()=>v(()=>import("./toc.html-sozxNRF7.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-2d0ad528":()=>v(()=>import("./index.html-ZH7R9G3s.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-6203866e":()=>v(()=>import("./index.html-g_k3EZSB.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-34d6c4ff":()=>v(()=>import("./channel.html-uSvMSuIK.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-31d2926e":()=>v(()=>import("./config.html-ZkgZP8Et.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-42f5c890":()=>v(()=>import("./frontmatter.html-tEEC6mym.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-619e6245":()=>v(()=>import("./getter.html-oPPSF0v5.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-2c2418e6":()=>v(()=>import("./guide.html-VwWU0xoR.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-873e9c0c":()=>v(()=>import("./index.html-0H9Nmgxz.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-1cc5a49d":()=>v(()=>import("./config.html-BVRB3Svi.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-4c7e84d7":()=>v(()=>import("./guide.html-eW87z6FH.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-37b048a2":()=>v(()=>import("./index.html-GUPgZrM2.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-24de5616":()=>v(()=>import("./config.html-fdGyVXQz.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-4fbe5591":()=>v(()=>import("./frontmatter.html-7uMpDeX9.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-30402d02":()=>v(()=>import("./guide.html-yQA_27h3.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-e78362cc":()=>v(()=>import("./index.html-cmeFmgvT.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-54b6b4ae":()=>v(()=>import("./components.html-5eAAqCkE.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-7ed43806":()=>v(()=>import("./config.html-tE5kUEV6.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3fd0046b":()=>v(()=>import("./extending.html-SKKh8LlA.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3e316b89":()=>v(()=>import("./frontmatter.html-cv35bZ-S.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-319287e5":()=>v(()=>import("./locale.html-ubQ9h3kZ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-66d0bcf0":()=>v(()=>import("./markdown.html-xk2DPo1W.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-15ee72a8":()=>v(()=>import("./plugin.html-CQ1xhZaI.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-392d5b46":()=>v(()=>import("./styles.html-6NsWkdvc.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-6981b9b0":()=>v(()=>import("./active-header-links.html-bF3r-Kk6.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-c1a628f2":()=>v(()=>import("./back-to-top.html-uUD76HWZ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5a457a6f":()=>v(()=>import("./container.html-Zx0agdiJ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5666a02b":()=>v(()=>import("./copy-code.html-knN80jzu.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-55f4eee9":()=>v(()=>import("./copyright.html-kHatrOgy.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-b372ed20":()=>v(()=>import("./docsearch.html-ea7pYjQo.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-f16906d0":()=>v(()=>import("./external-link-icon.html-gzX-ygIZ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-1db67204":()=>v(()=>import("./git.html-2parGuCs.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-6f323dd0":()=>v(()=>import("./google-analytics.html-GlGK1LpA.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-658e4845":()=>v(()=>import("./medium-zoom.html-axSoW9tP.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-63215295":()=>v(()=>import("./nprogress.html-PAodSekb.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-577d4e75":()=>v(()=>import("./palette.html-EejcQ78z.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-ad678bf4":()=>v(()=>import("./prismjs.html-GY0lJvRB.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-7bd28997":()=>v(()=>import("./pwa-popup.html-4su7q40y.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5c3a8496":()=>v(()=>import("./pwa.html-h9cZRfgZ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-549eb354":()=>v(()=>import("./reading-time.html-pIyUC7pP.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-eca22bb4":()=>v(()=>import("./redirect.html-qXZmrThg.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-a51d0de0":()=>v(()=>import("./register-components.html-K3STLtO8.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-444d9b1e":()=>v(()=>import("./remove-pwa.html-zBr2B-69.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-bfe10934":()=>v(()=>import("./rtl.html-IhpUR5z5.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-72a3dbcc":()=>v(()=>import("./search.html-3pAnfhg9.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3c435ffe":()=>v(()=>import("./shiki.html-eykzOzfX.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-f3866458":()=>v(()=>import("./theme-data.html-5YmilAfL.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-57f70f48":()=>v(()=>import("./toc.html-gNCC-lB2.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-21359efa":()=>v(()=>import("./index.html-dkUSQ5O2.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-87c134a0":()=>v(()=>import("./channel.html-Yo0Atm8m.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-bcead0c6":()=>v(()=>import("./config.html-UsevBMSw.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5a3bfbe9":()=>v(()=>import("./frontmatter.html-e8IdhucI.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5d533118":()=>v(()=>import("./getter.html-QerCAQ7D.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-71cf31d7":()=>v(()=>import("./guide.html-7nU4vNh-.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-60e874ae":()=>v(()=>import("./index.html-293UxFOu.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-6270bd8e":()=>v(()=>import("./config.html-Tqr51Z6v.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-678409c6":()=>v(()=>import("./guide.html-hXJzeUfu.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5a88265e":()=>v(()=>import("./index.html-nTgZ_LTS.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-7597e534":()=>v(()=>import("./config.html-Srk8n1d6.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-1631bbc0":()=>v(()=>import("./frontmatter.html-5APACrWs.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-49ab5eee":()=>v(()=>import("./guide.html-2sIkuS_O.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-2e25e88b":()=>v(()=>import("./index.html-HTigedTr.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-5e6c68d0":()=>v(()=>import("./components.html-xrmxA2y2.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-2261596c":()=>v(()=>import("./config.html-PhwKH_Cw.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-88f236c8":()=>v(()=>import("./extending.html-cRARANhQ.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-b09df90c":()=>v(()=>import("./frontmatter.html-cqZOVL8x.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-d9440558":()=>v(()=>import("./locale.html-_3zP9_4l.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-f8d6dac2":()=>v(()=>import("./markdown.html-jmp42nVr.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-56d43c1b":()=>v(()=>import("./plugin.html-o6xbQm-S.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-4534c7cc":()=>v(()=>import("./styles.html-wHbF_pty.js"),__vite__mapDeps([])).then(({data:e})=>e),"v-3706649a":()=>v(()=>import("./404.html-6Yl8cQE3.js"),__vite__mapDeps([])).then(({data:e})=>e)},Nu=JSON.parse('{"base":"/ecosystem/","lang":"en-US","title":"","description":"","head":[["link",{"rel":"icon","type":"image/png","sizes":"16x16","href":"/images/icons/favicon-16x16.png"}],["link",{"rel":"icon","type":"image/png","sizes":"32x32","href":"/images/icons/favicon-32x32.png"}],["link",{"rel":"manifest","href":"/manifest.webmanifest"}],["meta",{"name":"application-name","content":"VuePress"}],["meta",{"name":"apple-mobile-web-app-title","content":"VuePress"}],["meta",{"name":"apple-mobile-web-app-status-bar-style","content":"black"}],["link",{"rel":"apple-touch-icon","href":"/images/icons/apple-touch-icon.png"}],["link",{"rel":"mask-icon","href":"/images/icons/safari-pinned-tab.svg","color":"#3eaf7c"}],["meta",{"name":"msapplication-TileColor","content":"#3eaf7c"}],["meta",{"name":"theme-color","content":"#3eaf7c"}]],"locales":{"/":{"lang":"en-US","title":"VuePress Ecosystem","description":"VuePress official themes plugins"},"/zh/":{"lang":"zh-CN","title":"VuePress 生态系统","description":"VuePress 官方主题和插件"}}}');var Hu=["link","meta","script","style","noscript","template"],Fu=["title","base"],Bu=([e,t,n])=>Fu.includes(e)?e:Hu.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,ju=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=Bu(r);o&&!t.has(o)&&(t.add(o),n.push(r))}),n},Xn=e=>/^(https?:)?\/\//.test(e),Uu=e=>/^[a-z][a-z0-9+.-]*:/.test(e),Wo=e=>Object.prototype.toString.call(e)==="[object Object]",bl=e=>e[e.length-1]==="/"?e.slice(0,-1):e,El=e=>e[0]==="/"?e.slice(1):e,yl=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const s=o.split("/").length-r.split("/").length;return s!==0?s:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"},Wu=e=>typeof e=="function",dt=e=>typeof e=="string";const Ll={"v-8daa1a0e":x(()=>v(()=>import("./index.html-8D6ojdom.js"),__vite__mapDeps([]))),"v-b9e358fe":x(()=>v(()=>import("./active-header-links.html-7dYsqXKk.js"),__vite__mapDeps([]))),"v-2f74e658":x(()=>v(()=>import("./back-to-top.html-Lcuz8vr_.js"),__vite__mapDeps([]))),"v-3f3ff580":x(()=>v(()=>import("./container.html-IYbMD28v.js"),__vite__mapDeps([]))),"v-3b611b3c":x(()=>v(()=>import("./copy-code.html-3o0awFLj.js"),__vite__mapDeps([]))),"v-3aef69fa":x(()=>v(()=>import("./copyright.html-GOVOyHFB.js"),__vite__mapDeps([]))),"v-e97df6fe":x(()=>v(()=>import("./docsearch.html-mJ2H8sBU.js"),__vite__mapDeps([]))),"v-1dcbe4a7":x(()=>v(()=>import("./external-link-icon.html-pvvkRIA0.js"),__vite__mapDeps([]))),"v-561922cf":x(()=>v(()=>import("./git.html-j8z7sJkh.js"),__vite__mapDeps([]))),"v-190944c2":x(()=>v(()=>import("./google-analytics.html-0yWoZfLM.js"),__vite__mapDeps([]))),"v-145379d4":x(()=>v(()=>import("./medium-zoom.html-ztrN6D68.js"),__vite__mapDeps([]))),"v-481bcda6":x(()=>v(()=>import("./nprogress.html-cRlovdwN.js"),__vite__mapDeps([]))),"v-304d59c6":x(()=>v(()=>import("./palette.html-rQPFw1I-.js"),__vite__mapDeps([]))),"v-fbc77552":x(()=>v(()=>import("./prismjs.html-5BFkHxch.js"),__vite__mapDeps([]))),"v-60cd04a8":x(()=>v(()=>import("./pwa-popup.html-_RkFLcjG.js"),__vite__mapDeps([]))),"v-7da23f32":x(()=>v(()=>import("./pwa.html-9r2A4uTL.js"),__vite__mapDeps([]))),"v-6553daba":x(()=>v(()=>import("./reading-time.html-8A3DmWWk.js"),__vite__mapDeps([]))),"v-6a3f6e16":x(()=>v(()=>import("./redirect.html-3IGFLCWJ.js"),__vite__mapDeps([]))),"v-3203da3e":x(()=>v(()=>import("./register-components.html-kvj0B1Hx.js"),__vite__mapDeps([]))),"v-cfa3cd00":x(()=>v(()=>import("./remove-pwa.html-TF0wHWZS.js"),__vite__mapDeps([]))),"v-0503d737":x(()=>v(()=>import("./rtl.html-S66r9gJG.js"),__vite__mapDeps([]))),"v-12124eae":x(()=>v(()=>import("./search.html-jkn-b6oZ.js"),__vite__mapDeps([]))),"v-35900b8f":x(()=>v(()=>import("./shiki.html-z5Ugr0Bc.js"),__vite__mapDeps([]))),"v-4091b4e3":x(()=>v(()=>import("./theme-data.html-DRSBadNU.js"),__vite__mapDeps([]))),"v-862929ce":x(()=>v(()=>import("./toc.html-c4CKbH_A.js"),__vite__mapDeps([]))),"v-2d0ad528":x(()=>v(()=>import("./index.html-NqGL3aiX.js"),__vite__mapDeps([]))),"v-6203866e":x(()=>v(()=>import("./index.html-kAwxn97_.js"),__vite__mapDeps([]))),"v-34d6c4ff":x(()=>v(()=>import("./channel.html-k1lKhm8T.js"),__vite__mapDeps([]))),"v-31d2926e":x(()=>v(()=>import("./config.html-9JbPInT0.js"),__vite__mapDeps([]))),"v-42f5c890":x(()=>v(()=>import("./frontmatter.html-zRyVb7je.js"),__vite__mapDeps([]))),"v-619e6245":x(()=>v(()=>import("./getter.html-sACV4VqX.js"),__vite__mapDeps([]))),"v-2c2418e6":x(()=>v(()=>import("./guide.html-Jv9RswDw.js"),__vite__mapDeps([]))),"v-873e9c0c":x(()=>v(()=>import("./index.html-5SZj0ulP.js"),__vite__mapDeps([]))),"v-1cc5a49d":x(()=>v(()=>import("./config.html-3FJyrGZW.js"),__vite__mapDeps([]))),"v-4c7e84d7":x(()=>v(()=>import("./guide.html-MVe53ul9.js"),__vite__mapDeps([]))),"v-37b048a2":x(()=>v(()=>import("./index.html-NRSJldfv.js"),__vite__mapDeps([]))),"v-24de5616":x(()=>v(()=>import("./config.html-_tauS4zw.js"),__vite__mapDeps([]))),"v-4fbe5591":x(()=>v(()=>import("./frontmatter.html-r9qGaG_A.js"),__vite__mapDeps([]))),"v-30402d02":x(()=>v(()=>import("./guide.html-DKfq4qCI.js"),__vite__mapDeps([]))),"v-e78362cc":x(()=>v(()=>import("./index.html-aPphe-5B.js"),__vite__mapDeps([]))),"v-54b6b4ae":x(()=>v(()=>import("./components.html-CQjk7rXv.js"),__vite__mapDeps([]))),"v-7ed43806":x(()=>v(()=>import("./config.html-bGa-nBlX.js"),__vite__mapDeps([]))),"v-3fd0046b":x(()=>v(()=>import("./extending.html-TWRhgpOs.js"),__vite__mapDeps([0,1]))),"v-3e316b89":x(()=>v(()=>import("./frontmatter.html-uwXqgIn9.js"),__vite__mapDeps([]))),"v-319287e5":x(()=>v(()=>import("./locale.html-ayLbTzsN.js"),__vite__mapDeps([]))),"v-66d0bcf0":x(()=>v(()=>import("./markdown.html-gZt7th1N.js"),__vite__mapDeps([]))),"v-15ee72a8":x(()=>v(()=>import("./plugin.html-o0jzXsiQ.js"),__vite__mapDeps([]))),"v-392d5b46":x(()=>v(()=>import("./styles.html-za_WSAv-.js"),__vite__mapDeps([]))),"v-6981b9b0":x(()=>v(()=>import("./active-header-links.html-WdtOWGzn.js"),__vite__mapDeps([]))),"v-c1a628f2":x(()=>v(()=>import("./back-to-top.html-sQ7U_G8N.js"),__vite__mapDeps([]))),"v-5a457a6f":x(()=>v(()=>import("./container.html-CxVtbIlg.js"),__vite__mapDeps([]))),"v-5666a02b":x(()=>v(()=>import("./copy-code.html-7RjOj2Mn.js"),__vite__mapDeps([]))),"v-55f4eee9":x(()=>v(()=>import("./copyright.html-4w5HDbhl.js"),__vite__mapDeps([]))),"v-b372ed20":x(()=>v(()=>import("./docsearch.html-XgUI2xOq.js"),__vite__mapDeps([]))),"v-f16906d0":x(()=>v(()=>import("./external-link-icon.html-wC4QqVHc.js"),__vite__mapDeps([]))),"v-1db67204":x(()=>v(()=>import("./git.html-gfEIsrAW.js"),__vite__mapDeps([]))),"v-6f323dd0":x(()=>v(()=>import("./google-analytics.html-QjESXOwf.js"),__vite__mapDeps([]))),"v-658e4845":x(()=>v(()=>import("./medium-zoom.html-7wkriUuL.js"),__vite__mapDeps([]))),"v-63215295":x(()=>v(()=>import("./nprogress.html-QuAXox5S.js"),__vite__mapDeps([]))),"v-577d4e75":x(()=>v(()=>import("./palette.html-jilTbvVt.js"),__vite__mapDeps([]))),"v-ad678bf4":x(()=>v(()=>import("./prismjs.html-G8UKOPdU.js"),__vite__mapDeps([]))),"v-7bd28997":x(()=>v(()=>import("./pwa-popup.html-tGZDCjGz.js"),__vite__mapDeps([]))),"v-5c3a8496":x(()=>v(()=>import("./pwa.html-p5uqsdnO.js"),__vite__mapDeps([]))),"v-549eb354":x(()=>v(()=>import("./reading-time.html--6neugEt.js"),__vite__mapDeps([]))),"v-eca22bb4":x(()=>v(()=>import("./redirect.html-zpA2Lu7N.js"),__vite__mapDeps([]))),"v-a51d0de0":x(()=>v(()=>import("./register-components.html-hbe_pzQO.js"),__vite__mapDeps([]))),"v-444d9b1e":x(()=>v(()=>import("./remove-pwa.html-whrdIVXP.js"),__vite__mapDeps([]))),"v-bfe10934":x(()=>v(()=>import("./rtl.html-lfnNHMRj.js"),__vite__mapDeps([]))),"v-72a3dbcc":x(()=>v(()=>import("./search.html-YtY13_8l.js"),__vite__mapDeps([]))),"v-3c435ffe":x(()=>v(()=>import("./shiki.html-bqkMqjP9.js"),__vite__mapDeps([]))),"v-f3866458":x(()=>v(()=>import("./theme-data.html-ufOo3grp.js"),__vite__mapDeps([]))),"v-57f70f48":x(()=>v(()=>import("./toc.html-t9K5uGlI.js"),__vite__mapDeps([]))),"v-21359efa":x(()=>v(()=>import("./index.html-flT6MTap.js"),__vite__mapDeps([]))),"v-87c134a0":x(()=>v(()=>import("./channel.html-97Rgn5HR.js"),__vite__mapDeps([]))),"v-bcead0c6":x(()=>v(()=>import("./config.html-eFCZds2u.js"),__vite__mapDeps([]))),"v-5a3bfbe9":x(()=>v(()=>import("./frontmatter.html-APRPiTDP.js"),__vite__mapDeps([]))),"v-5d533118":x(()=>v(()=>import("./getter.html-AJzL42Au.js"),__vite__mapDeps([]))),"v-71cf31d7":x(()=>v(()=>import("./guide.html-mvLKR8iA.js"),__vite__mapDeps([]))),"v-60e874ae":x(()=>v(()=>import("./index.html-Ugn7-yHs.js"),__vite__mapDeps([]))),"v-6270bd8e":x(()=>v(()=>import("./config.html-bycs_hrt.js"),__vite__mapDeps([]))),"v-678409c6":x(()=>v(()=>import("./guide.html-HoiurzPe.js"),__vite__mapDeps([]))),"v-5a88265e":x(()=>v(()=>import("./index.html-BvtKXxHU.js"),__vite__mapDeps([]))),"v-7597e534":x(()=>v(()=>import("./config.html-LeVTWPLA.js"),__vite__mapDeps([]))),"v-1631bbc0":x(()=>v(()=>import("./frontmatter.html-onFKhKev.js"),__vite__mapDeps([]))),"v-49ab5eee":x(()=>v(()=>import("./guide.html-DJ-MSZJF.js"),__vite__mapDeps([]))),"v-2e25e88b":x(()=>v(()=>import("./index.html-31AzVlYj.js"),__vite__mapDeps([]))),"v-5e6c68d0":x(()=>v(()=>import("./components.html-GZgKoQnq.js"),__vite__mapDeps([]))),"v-2261596c":x(()=>v(()=>import("./config.html-k_L4yq_7.js"),__vite__mapDeps([]))),"v-88f236c8":x(()=>v(()=>import("./extending.html-EihhNNxm.js"),__vite__mapDeps([2,1]))),"v-b09df90c":x(()=>v(()=>import("./frontmatter.html-mMjdstQ1.js"),__vite__mapDeps([]))),"v-d9440558":x(()=>v(()=>import("./locale.html-umfeNPJn.js"),__vite__mapDeps([]))),"v-f8d6dac2":x(()=>v(()=>import("./markdown.html-YIelTe7Z.js"),__vite__mapDeps([]))),"v-56d43c1b":x(()=>v(()=>import("./plugin.html-w_pj_Syc.js"),__vite__mapDeps([]))),"v-4534c7cc":x(()=>v(()=>import("./styles.html-MtQFW6Eq.js"),__vite__mapDeps([]))),"v-3706649a":x(()=>v(()=>import("./404.html-K60eDWvA.js"),__vite__mapDeps([])))};var Ku=Symbol(""),wl=Symbol(""),qu=Wn({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Zt=()=>{const e=Pe(wl);if(!e)throw new Error("pageData() is called without provider.");return e},Tl=Symbol(""),ht=()=>{const e=Pe(Tl);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},Al=Symbol(""),Gu=()=>{const e=Pe(Al);if(!e)throw new Error("usePageHead() is called without provider.");return e},Yu=Symbol(""),Pl=Symbol(""),Ju=()=>{const e=Pe(Pl);if(!e)throw new Error("usePageLang() is called without provider.");return e},Rl=Symbol(""),Xu=()=>{const e=Pe(Rl);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Qu=ue(Mu),Ko=Symbol(""),Qn=()=>{const e=Pe(Ko);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},an=ue(Nu),Ol=()=>an,Cl=Symbol(""),qo=()=>{const e=Pe(Cl);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},Zu=Symbol(""),ef="Layout",tf="NotFound",mt=Un({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageData:async e=>{const t=Qu.value[e];return await(t==null?void 0:t())??qu},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,n)=>{const r=dt(t.description)?t.description:n.description,o=[...Array.isArray(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return ju(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let n;if(e.path){const r=e.frontmatter.layout;dt(r)?n=r:n=ef}else n=tf;return t[n]},resolveRouteLocale:(e,t)=>yl(e,t),resolveSiteLocaleData:(e,t)=>{var n;return{...e,...e.locales[t],head:[...((n=e.locales[t])==null?void 0:n.head)??[],...e.head??[]]}}}),Go=me({name:"ClientOnly",setup(e,t){const n=ue(!1);return We(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),nf=me({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=Zt(),n=N(()=>Ll[e.pageKey||t.value.key]);return()=>n.value?_e(n.value):_e("div","404 Not Found")}}),Ft=(e={})=>e,Yo=e=>Xn(e)?e:`/ecosystem/${El(e)}`;function rf(e,t,n){var r,o,s;t===void 0&&(t=50),n===void 0&&(n={});var i=(r=n.isImmediate)!=null&&r,l=(o=n.callback)!=null&&o,a=n.maxWait,c=Date.now(),u=[];function f(){if(a!==void 0){var m=Date.now()-c;if(m+t>=a)return a-m}return t}var d=function(){var m=[].slice.call(arguments),g=this;return new Promise(function(y,L){var A=i&&s===void 0;if(s!==void 0&&clearTimeout(s),s=setTimeout(function(){if(s=void 0,c=Date.now(),!i){var _=e.apply(g,m);l&&l(_),u.forEach(function(P){return(0,P.resolve)(_)}),u=[]}},f()),A){var C=e.apply(g,m);return l&&l(C),y(C)}u.push({resolve:y,reject:L})})};return d.cancel=function(m){s!==void 0&&clearTimeout(s),u.forEach(function(g){return(0,g.reject)(m)}),u=[]},d}/*! + * vue-router v4.2.5 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */const ln=typeof window<"u";function of(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ve=Object.assign;function to(e,t){const n={};for(const r in t){const o=t[r];n[r]=lt(o)?o.map(e):e(o)}return n}const Sn=()=>{},lt=Array.isArray,sf=/\/$/,lf=e=>e.replace(sf,"");function no(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("#");let a=t.indexOf("?");return l=0&&(a=-1),a>-1&&(r=t.slice(0,a),s=t.slice(a+1,l>-1?l:t.length),o=e(s)),l>-1&&(r=r||t.slice(0,l),i=t.slice(l,t.length)),r=ff(r??t,n),{fullPath:r+(s&&"?")+s+i,path:r,query:o,hash:i}}function af(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Ks(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function cf(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&vn(t.matched[r],n.matched[o])&&Il(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function vn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Il(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!uf(e[n],t[n]))return!1;return!0}function uf(e,t){return lt(e)?qs(e,t):lt(t)?qs(t,e):e===t}function qs(e,t){return lt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function ff(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 s=n.length-1,i,l;for(i=0;i1&&s--;else break;return n.slice(0,s).join("/")+"/"+r.slice(i-(i===r.length?1:0)).join("/")}var Nn;(function(e){e.pop="pop",e.push="push"})(Nn||(Nn={}));var Dn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Dn||(Dn={}));function df(e){if(!e)if(ln){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),lf(e)}const hf=/^[^#]+#/;function pf(e,t){return e.replace(hf,"#")+t}function mf(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 Nr=()=>({left:window.pageXOffset,top:window.pageYOffset});function vf(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=mf(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Gs(e,t){return(history.state?history.state.position-t:-1)+e}const Lo=new Map;function _f(e,t){Lo.set(e,t)}function gf(e){const t=Lo.get(e);return Lo.delete(e),t}let bf=()=>location.protocol+"//"+location.host;function xl(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let l=o.includes(e.slice(s))?e.slice(s).length:1,a=o.slice(l);return a[0]!=="/"&&(a="/"+a),Ks(a,"")}return Ks(n,e)+r+o}function Ef(e,t,n,r){let o=[],s=[],i=null;const l=({state:d})=>{const m=xl(e,location),g=n.value,y=t.value;let L=0;if(d){if(n.value=m,t.value=d,i&&i===g){i=null;return}L=y?d.position-y.position:0}else r(m);o.forEach(A=>{A(n.value,g,{delta:L,type:Nn.pop,direction:L?L>0?Dn.forward:Dn.back:Dn.unknown})})};function a(){i=n.value}function c(d){o.push(d);const m=()=>{const g=o.indexOf(d);g>-1&&o.splice(g,1)};return s.push(m),m}function u(){const{history:d}=window;d.state&&d.replaceState(ve({},d.state,{scroll:Nr()}),"")}function f(){for(const d of s)d();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:a,listen:c,destroy:f}}function Ys(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?Nr():null}}function yf(e){const{history:t,location:n}=window,r={value:xl(e,n)},o={value:t.state};o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(a,c,u){const f=e.indexOf("#"),d=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+a:bf()+e+a;try{t[u?"replaceState":"pushState"](c,"",d),o.value=c}catch(m){console.error(m),n[u?"replace":"assign"](d)}}function i(a,c){const u=ve({},t.state,Ys(o.value.back,a,o.value.forward,!0),c,{position:o.value.position});s(a,u,!0),r.value=a}function l(a,c){const u=ve({},o.value,t.state,{forward:a,scroll:Nr()});s(u.current,u,!0);const f=ve({},Ys(r.value,a,null),{position:u.position+1},c);s(a,f,!1),r.value=a}return{location:r,state:o,push:l,replace:i}}function Lf(e){e=df(e);const t=yf(e),n=Ef(e,t.state,t.location,t.replace);function r(s,i=!0){i||n.pauseListeners(),history.go(s)}const o=ve({location:"",base:e,go:r,createHref:pf.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 wf(e){return typeof e=="string"||e&&typeof e=="object"}function Sl(e){return typeof e=="string"||typeof e=="symbol"}const vt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Dl=Symbol("");var Js;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Js||(Js={}));function _n(e,t){return ve(new Error,{type:e,[Dl]:!0},t)}function pt(e,t){return e instanceof Error&&Dl in e&&(t==null||!!(e.type&t))}const Xs="[^/]+?",Tf={sensitive:!1,strict:!1,start:!0,end:!0},Af=/[.+*?^${}()[\]/\\]/g;function Pf(e,t){const n=ve({},Tf,t),r=[];let o=n.start?"^":"";const s=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(o+="/");for(let f=0;ft.length?t.length===1&&t[0]===80?1:-1:0}function Of(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const Cf={type:0,value:""},If=/[a-zA-Z0-9_]/;function xf(e){if(!e)return[[]];if(e==="/")return[[Cf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(m){throw new Error(`ERR (${n})/"${c}": ${m}`)}let n=0,r=n;const o=[];let s;function i(){s&&o.push(s),s=[]}let l=0,a,c="",u="";function f(){c&&(n===0?s.push({type:0,value:c}):n===1||n===2||n===3?(s.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=a}for(;l{i(C)}:Sn}function i(u){if(Sl(u)){const f=r.get(u);f&&(r.delete(u),n.splice(n.indexOf(f),1),f.children.forEach(i),f.alias.forEach(i))}else{const f=n.indexOf(u);f>-1&&(n.splice(f,1),u.record.name&&r.delete(u.record.name),u.children.forEach(i),u.alias.forEach(i))}}function l(){return n}function a(u){let f=0;for(;f=0&&(u.record.path!==n[f].record.path||!kl(u,n[f]));)f++;n.splice(f,0,u),u.record.name&&!ei(u)&&r.set(u.record.name,u)}function c(u,f){let d,m={},g,y;if("name"in u&&u.name){if(d=r.get(u.name),!d)throw _n(1,{location:u});y=d.record.name,m=ve(Zs(f.params,d.keys.filter(C=>!C.optional).map(C=>C.name)),u.params&&Zs(u.params,d.keys.map(C=>C.name))),g=d.stringify(m)}else if("path"in u)g=u.path,d=n.find(C=>C.re.test(g)),d&&(m=d.parse(g),y=d.record.name);else{if(d=f.name?r.get(f.name):n.find(C=>C.re.test(f.path)),!d)throw _n(1,{location:u,currentLocation:f});y=d.record.name,m=ve({},f.params,u.params),g=d.stringify(m)}const L=[];let A=d;for(;A;)L.unshift(A.record),A=A.parent;return{name:y,path:g,params:m,matched:L,meta:Vf(L)}}return e.forEach(u=>s(u)),{addRoute:s,resolve:c,removeRoute:i,getRoutes:l,getRecordMatcher:o}}function Zs(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function kf(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:zf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function zf(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 ei(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Vf(e){return e.reduce((t,n)=>ve(t,n.meta),{})}function ti(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function kl(e,t){return t.children.some(n=>n===e||kl(e,n))}const zl=/#/g,$f=/&/g,Mf=/\//g,Nf=/=/g,Hf=/\?/g,Vl=/\+/g,Ff=/%5B/g,Bf=/%5D/g,$l=/%5E/g,jf=/%60/g,Ml=/%7B/g,Uf=/%7C/g,Nl=/%7D/g,Wf=/%20/g;function Jo(e){return encodeURI(""+e).replace(Uf,"|").replace(Ff,"[").replace(Bf,"]")}function Kf(e){return Jo(e).replace(Ml,"{").replace(Nl,"}").replace($l,"^")}function wo(e){return Jo(e).replace(Vl,"%2B").replace(Wf,"+").replace(zl,"%23").replace($f,"%26").replace(jf,"`").replace(Ml,"{").replace(Nl,"}").replace($l,"^")}function qf(e){return wo(e).replace(Nf,"%3D")}function Gf(e){return Jo(e).replace(zl,"%23").replace(Hf,"%3F")}function Yf(e){return e==null?"":Gf(e).replace(Mf,"%2F")}function Rr(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Jf(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;os&&wo(s)):[r&&wo(r)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function Xf(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=lt(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const Qf=Symbol(""),ri=Symbol(""),Hr=Symbol(""),Xo=Symbol(""),To=Symbol("");function Tn(){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 xt(e,t,n,r,o){const s=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((i,l)=>{const a=f=>{f===!1?l(_n(4,{from:n,to:t})):f instanceof Error?l(f):wf(f)?l(_n(2,{from:t,to:f})):(s&&r.enterCallbacks[o]===s&&typeof f=="function"&&s.push(f),i())},c=e.call(r&&r.instances[o],t,n,a);let u=Promise.resolve(c);e.length<3&&(u=u.then(a)),u.catch(f=>l(f))})}function ro(e,t,n,r){const o=[];for(const s of e)for(const i in s.components){let l=s.components[i];if(!(t!=="beforeRouteEnter"&&!s.instances[i]))if(Zf(l)){const c=(l.__vccOpts||l)[t];c&&o.push(xt(c,n,r,s,i))}else{let a=l();o.push(()=>a.then(c=>{if(!c)return Promise.reject(new Error(`Couldn't resolve component "${i}" at "${s.path}"`));const u=of(c)?c.default:c;s.components[i]=u;const d=(u.__vccOpts||u)[t];return d&&xt(d,n,r,s,i)()}))}}return o}function Zf(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function oi(e){const t=Pe(Hr),n=Pe(Xo),r=N(()=>t.resolve(te(e.to))),o=N(()=>{const{matched:a}=r.value,{length:c}=a,u=a[c-1],f=n.matched;if(!u||!f.length)return-1;const d=f.findIndex(vn.bind(null,u));if(d>-1)return d;const m=si(a[c-2]);return c>1&&si(u)===m&&f[f.length-1].path!==m?f.findIndex(vn.bind(null,a[c-2])):d}),s=N(()=>o.value>-1&&rd(n.params,r.value.params)),i=N(()=>o.value>-1&&o.value===n.matched.length-1&&Il(n.params,r.value.params));function l(a={}){return nd(a)?t[te(e.replace)?"replace":"push"](te(e.to)).catch(Sn):Promise.resolve()}return{route:r,href:N(()=>r.value.href),isActive:s,isExactActive:i,navigate:l}}const ed=me({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:oi,setup(e,{slots:t}){const n=Un(oi(e)),{options:r}=Pe(Hr),o=N(()=>({[ii(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[ii(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:_e("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},s)}}}),td=ed;function nd(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 rd(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(!lt(o)||o.length!==r.length||r.some((s,i)=>s!==o[i]))return!1}return!0}function si(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const ii=(e,t,n)=>e??t??n,od=me({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=Pe(To),o=N(()=>e.route||r.value),s=Pe(ri,0),i=N(()=>{let c=te(s);const{matched:u}=o.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),l=N(()=>o.value.matched[i.value]);Qt(ri,N(()=>i.value+1)),Qt(Qf,l),Qt(To,o);const a=ue();return He(()=>[a.value,l.value,e.name],([c,u,f],[d,m,g])=>{u&&(u.instances[f]=c,m&&m!==u&&c&&c===d&&(u.leaveGuards.size||(u.leaveGuards=m.leaveGuards),u.updateGuards.size||(u.updateGuards=m.updateGuards))),c&&u&&(!m||!vn(u,m)||!d)&&(u.enterCallbacks[f]||[]).forEach(y=>y(c))},{flush:"post"}),()=>{const c=o.value,u=e.name,f=l.value,d=f&&f.components[u];if(!d)return li(n.default,{Component:d,route:c});const m=f.props[u],g=m?m===!0?c.params:typeof m=="function"?m(c):m:null,L=_e(d,ve({},g,t,{onVnodeUnmounted:A=>{A.component.isUnmounted&&(f.instances[u]=null)},ref:a}));return li(n.default,{Component:L,route:c})||L}}});function li(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Hl=od;function sd(e){const t=Df(e.routes,e),n=e.parseQuery||Jf,r=e.stringifyQuery||ni,o=e.history,s=Tn(),i=Tn(),l=Tn(),a=$o(vt);let c=vt;ln&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=to.bind(null,R=>""+R),f=to.bind(null,Yf),d=to.bind(null,Rr);function m(R,K){let j,J;return Sl(R)?(j=t.getRecordMatcher(R),J=K):J=R,t.addRoute(J,j)}function g(R){const K=t.getRecordMatcher(R);K&&t.removeRoute(K)}function y(){return t.getRoutes().map(R=>R.record)}function L(R){return!!t.getRecordMatcher(R)}function A(R,K){if(K=ve({},K||a.value),typeof R=="string"){const p=no(n,R,K.path),E=t.resolve({path:p.path},K),O=o.createHref(p.fullPath);return ve(p,E,{params:d(E.params),hash:Rr(p.hash),redirectedFrom:void 0,href:O})}let j;if("path"in R)j=ve({},R,{path:no(n,R.path,K.path).path});else{const p=ve({},R.params);for(const E in p)p[E]==null&&delete p[E];j=ve({},R,{params:f(p)}),K.params=f(K.params)}const J=t.resolve(j,K),ce=R.hash||"";J.params=u(d(J.params));const ge=af(r,ve({},R,{hash:Kf(ce),path:J.path})),h=o.createHref(ge);return ve({fullPath:ge,hash:ce,query:r===ni?Xf(R.query):R.query||{}},J,{redirectedFrom:void 0,href:h})}function C(R){return typeof R=="string"?no(n,R,a.value.path):ve({},R)}function _(R,K){if(c!==R)return _n(8,{from:K,to:R})}function P(R){return M(R)}function F(R){return P(ve(C(R),{replace:!0}))}function H(R){const K=R.matched[R.matched.length-1];if(K&&K.redirect){const{redirect:j}=K;let J=typeof j=="function"?j(R):j;return typeof J=="string"&&(J=J.includes("?")||J.includes("#")?J=C(J):{path:J},J.params={}),ve({query:R.query,hash:R.hash,params:"path"in J?{}:R.params},J)}}function M(R,K){const j=c=A(R),J=a.value,ce=R.state,ge=R.force,h=R.replace===!0,p=H(j);if(p)return M(ve(C(p),{state:typeof p=="object"?ve({},ce,p.state):ce,force:ge,replace:h}),K||j);const E=j;E.redirectedFrom=K;let O;return!ge&&cf(r,J,j)&&(O=_n(16,{to:E,from:J}),Ke(J,J,!0,!1)),(O?Promise.resolve(O):I(E,J)).catch(T=>pt(T)?pt(T,2)?T:$e(T):q(T,E,J)).then(T=>{if(T){if(pt(T,2))return M(ve({replace:h},C(T.to),{state:typeof T.to=="object"?ve({},ce,T.to.state):ce,force:ge}),K||E)}else T=w(E,J,!0,h,ce);return Y(E,J,T),T})}function b(R,K){const j=_(R,K);return j?Promise.reject(j):Promise.resolve()}function k(R){const K=Lt.values().next().value;return K&&typeof K.runWithContext=="function"?K.runWithContext(R):R()}function I(R,K){let j;const[J,ce,ge]=id(R,K);j=ro(J.reverse(),"beforeRouteLeave",R,K);for(const p of J)p.leaveGuards.forEach(E=>{j.push(xt(E,R,K))});const h=b.bind(null,R,K);return j.push(h),ke(j).then(()=>{j=[];for(const p of s.list())j.push(xt(p,R,K));return j.push(h),ke(j)}).then(()=>{j=ro(ce,"beforeRouteUpdate",R,K);for(const p of ce)p.updateGuards.forEach(E=>{j.push(xt(E,R,K))});return j.push(h),ke(j)}).then(()=>{j=[];for(const p of ge)if(p.beforeEnter)if(lt(p.beforeEnter))for(const E of p.beforeEnter)j.push(xt(E,R,K));else j.push(xt(p.beforeEnter,R,K));return j.push(h),ke(j)}).then(()=>(R.matched.forEach(p=>p.enterCallbacks={}),j=ro(ge,"beforeRouteEnter",R,K),j.push(h),ke(j))).then(()=>{j=[];for(const p of i.list())j.push(xt(p,R,K));return j.push(h),ke(j)}).catch(p=>pt(p,8)?p:Promise.reject(p))}function Y(R,K,j){l.list().forEach(J=>k(()=>J(R,K,j)))}function w(R,K,j,J,ce){const ge=_(R,K);if(ge)return ge;const h=K===vt,p=ln?history.state:{};j&&(J||h?o.replace(R.fullPath,ve({scroll:h&&p&&p.scroll},ce)):o.push(R.fullPath,ce)),a.value=R,Ke(R,K,j,h),$e()}let $;function re(){$||($=o.listen((R,K,j)=>{if(!at.listening)return;const J=A(R),ce=H(J);if(ce){M(ve(ce,{replace:!0}),J).catch(Sn);return}c=J;const ge=a.value;ln&&_f(Gs(ge.fullPath,j.delta),Nr()),I(J,ge).catch(h=>pt(h,12)?h:pt(h,2)?(M(h.to,J).then(p=>{pt(p,20)&&!j.delta&&j.type===Nn.pop&&o.go(-1,!1)}).catch(Sn),Promise.reject()):(j.delta&&o.go(-j.delta,!1),q(h,J,ge))).then(h=>{h=h||w(J,ge,!1),h&&(j.delta&&!pt(h,8)?o.go(-j.delta,!1):j.type===Nn.pop&&pt(h,20)&&o.go(-1,!1)),Y(J,ge,h)}).catch(Sn)}))}let ie=Tn(),D=Tn(),X;function q(R,K,j){$e(R);const J=D.list();return J.length?J.forEach(ce=>ce(R,K,j)):console.error(R),Promise.reject(R)}function De(){return X&&a.value!==vt?Promise.resolve():new Promise((R,K)=>{ie.add([R,K])})}function $e(R){return X||(X=!R,re(),ie.list().forEach(([K,j])=>R?j(R):K()),ie.reset()),R}function Ke(R,K,j,J){const{scrollBehavior:ce}=e;if(!ln||!ce)return Promise.resolve();const ge=!j&&gf(Gs(R.fullPath,0))||(J||!j)&&history.state&&history.state.scroll||null;return bn().then(()=>ce(R,K,ge)).then(h=>h&&vf(h)).catch(h=>q(h,R,K))}const Fe=R=>o.go(R);let yt;const Lt=new Set,at={currentRoute:a,listening:!0,addRoute:m,removeRoute:g,hasRoute:L,getRoutes:y,resolve:A,options:e,push:P,replace:F,go:Fe,back:()=>Fe(-1),forward:()=>Fe(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:D.add,isReady:De,install(R){const K=this;R.component("RouterLink",td),R.component("RouterView",Hl),R.config.globalProperties.$router=K,Object.defineProperty(R.config.globalProperties,"$route",{enumerable:!0,get:()=>te(a)}),ln&&!yt&&a.value===vt&&(yt=!0,P(o.location).catch(ce=>{}));const j={};for(const ce in vt)Object.defineProperty(j,ce,{get:()=>a.value[ce],enumerable:!0});R.provide(Hr,K),R.provide(Xo,Ni(j)),R.provide(To,a);const J=R.unmount;Lt.add(R),R.unmount=function(){Lt.delete(R),Lt.size<1&&(c=vt,$&&$(),$=null,a.value=vt,yt=!1,X=!1),J()}}};function ke(R){return R.reduce((K,j)=>K.then(()=>k(j)),Promise.resolve())}return at}function id(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matched.length);for(let i=0;ivn(c,l))?r.push(l):n.push(l));const a=e.matched[i];a&&(t.matched.find(c=>vn(c,a))||o.push(a))}return[n,r,o]}function Mt(){return Pe(Hr)}function En(){return Pe(Xo)}const ld=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=Mt(),i=rf(()=>{var y,L;const l=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(l-0)d.some(C=>C.hash===A.hash));for(let A=0;A=(((y=C.parentElement)==null?void 0:y.offsetTop)??0)-r,F=!_||l<(((L=_.parentElement)==null?void 0:L.offsetTop)??0)-r;if(!(P&&F))continue;const M=decodeURIComponent(o.currentRoute.value.hash),b=decodeURIComponent(C.hash);if(M===b)return;if(f){for(let k=A+1;k{window.addEventListener("scroll",i)}),kr(()=>{window.removeEventListener("scroll",i)})},ai=async(e,t)=>{const{scrollBehavior:n}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=n)},ad="a.sidebar-item",cd=".header-anchor",ud=300,fd=5,dd=Ft({setup(){ld({headerLinkSelector:ad,headerAnchorSelector:cd,delay:ud,offset:fd})}}),Fl=e=>{const t=Qn();return N(()=>e[t.value]??{})},oo=(e,t)=>{var r;const n=(r=(t==null?void 0:t._instance)||$r())==null?void 0:r.appContext.components;return n?e in n||rt(e)in n||Bn(rt(e))in n:!1};function hd(e,t){let n,r,o;const s=ue(!0),i=()=>{s.value=!0,o()};He(e,i,{flush:"sync"});const l=typeof t=="function"?t:t.get,a=typeof t=="function"?void 0:t.set,c=Ka((u,f)=>(r=u,o=f,{get(){return s.value&&(n=l(),s.value=!1),r(),n},set(d){a==null||a(d)}}));return Object.isExtensible(c)&&(c.trigger=i),c}function Fr(e){return Pi()?(ya(e),!0):!1}function Nt(e){return typeof e=="function"?e():te(e)}const Qo=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const pd=Object.prototype.toString,md=e=>pd.call(e)==="[object Object]",vd=()=>{};function _d(e,t){function n(...r){return new Promise((o,s)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(s)})}return n}const Bl=e=>e();function gd(e=Bl){const t=ue(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...s)=>{t.value&&e(...s)};return{isActive:Wn(t),pause:n,resume:r,eventFilter:o}}function bd(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 Ed(e){return e||$r()}function yd(e,t,n={}){const{eventFilter:r=Bl,...o}=n;return He(e,_d(r,t),o)}function Ld(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:s,pause:i,resume:l,isActive:a}=gd(r);return{stop:yd(e,t,{...o,eventFilter:s}),pause:i,resume:l,isActive:a}}function Zo(e,t=!0,n){Ed()?We(e,n):t?e():bn(e)}function wd(e,t,n={}){const{immediate:r=!0}=n,o=ue(!1);let s=null;function i(){s&&(clearTimeout(s),s=null)}function l(){o.value=!1,i()}function a(...c){i(),o.value=!0,s=setTimeout(()=>{o.value=!1,s=null,e(...c)},Nt(t))}return r&&(o.value=!0,Qo&&a()),Fr(l),{isPending:Wn(o),start:a,stop:l}}function Td(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=je(e),s=ue(e);function i(l){if(arguments.length)return s.value=l,s.value;{const a=Nt(n);return s.value=s.value===a?Nt(r):a,s.value}}return o?i:[s,i]}function Yt(e){var t;const n=Nt(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Ht=Qo?window:void 0,jl=Qo?window.navigator:void 0;function en(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=Ht):[t,n,r,o]=e,!t)return vd;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const s=[],i=()=>{s.forEach(u=>u()),s.length=0},l=(u,f,d,m)=>(u.addEventListener(f,d,m),()=>u.removeEventListener(f,d,m)),a=He(()=>[Yt(t),Nt(o)],([u,f])=>{if(i(),!u)return;const d=md(f)?{...f}:f;s.push(...n.flatMap(m=>r.map(g=>l(u,m,g,d))))},{immediate:!0,flush:"post"}),c=()=>{a(),i()};return Fr(c),c}function Ad(){const e=ue(!1);return $r()&&We(()=>{e.value=!0}),e}function Br(e){const t=Ad();return N(()=>(t.value,!!e()))}function Ul(e,t={}){const{window:n=Ht}=t,r=Br(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const s=ue(!1),i=c=>{s.value=c.matches},l=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",i):o.removeListener(i))},a=uc(()=>{r.value&&(l(),o=n.matchMedia(Nt(e)),"addEventListener"in o?o.addEventListener("change",i):o.addListener(i),s.value=o.matches)});return Fr(()=>{a(),l(),o=void 0}),s}function ci(e,t={}){const{controls:n=!1,navigator:r=jl}=t,o=Br(()=>r&&"permissions"in r);let s;const i=typeof e=="string"?{name:e}:e,l=ue(),a=()=>{s&&(l.value=s.state)},c=bd(async()=>{if(o.value){if(!s)try{s=await r.permissions.query(i),en(s,"change",a),a()}catch{l.value="prompt"}return s}});return c(),n?{state:l,isSupported:o,query:c}:l}function Pd(e={}){const{navigator:t=jl,read:n=!1,source:r,copiedDuring:o=1500,legacy:s=!1}=e,i=Br(()=>t&&"clipboard"in t),l=ci("clipboard-read"),a=ci("clipboard-write"),c=N(()=>i.value||s),u=ue(""),f=ue(!1),d=wd(()=>f.value=!1,o);function m(){i.value&&l.value!=="denied"?t.clipboard.readText().then(A=>{u.value=A}):u.value=L()}c.value&&n&&en(["copy","cut"],m);async function g(A=Nt(r)){c.value&&A!=null&&(i.value&&a.value!=="denied"?await t.clipboard.writeText(A):y(A),u.value=A,f.value=!0,d.start())}function y(A){const C=document.createElement("textarea");C.value=A??"",C.style.position="absolute",C.style.opacity="0",document.body.appendChild(C),C.select(),document.execCommand("copy"),C.remove()}function L(){var A,C,_;return(_=(C=(A=document==null?void 0:document.getSelection)==null?void 0:A.call(document))==null?void 0:C.toString())!=null?_:""}return{isSupported:c,text:u,copied:f,copy:g}}const ar=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},cr="__vueuse_ssr_handlers__",Rd=Od();function Od(){return cr in ar||(ar[cr]=ar[cr]||{}),ar[cr]}function Cd(e,t){return Rd[e]||t}function Id(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 xd={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()}},ui="vueuse-storage";function Wl(e,t,n,r={}){var o;const{flush:s="pre",deep:i=!0,listenToStorageChanges:l=!0,writeDefaults:a=!0,mergeDefaults:c=!1,shallow:u,window:f=Ht,eventFilter:d,onError:m=k=>{console.error(k)},initOnMounted:g}=r,y=(u?$o:ue)(typeof t=="function"?t():t);if(!n)try{n=Cd("getDefaultStorage",()=>{var k;return(k=Ht)==null?void 0:k.localStorage})()}catch(k){m(k)}if(!n)return y;const L=Nt(t),A=Id(L),C=(o=r.serializer)!=null?o:xd[A],{pause:_,resume:P}=Ld(y,()=>F(y.value),{flush:s,deep:i,eventFilter:d});return f&&l&&Zo(()=>{en(f,"storage",b),en(f,ui,M),g&&b()}),g||b(),y;function F(k){try{if(k==null)n.removeItem(e);else{const I=C.write(k),Y=n.getItem(e);Y!==I&&(n.setItem(e,I),f&&f.dispatchEvent(new CustomEvent(ui,{detail:{key:e,oldValue:Y,newValue:I,storageArea:n}})))}}catch(I){m(I)}}function H(k){const I=k?k.newValue:n.getItem(e);if(I==null)return a&&L!=null&&n.setItem(e,C.write(L)),L;if(!k&&c){const Y=C.read(I);return typeof c=="function"?c(Y,L):A==="object"&&!Array.isArray(Y)?{...L,...Y}:Y}else return typeof I!="string"?I:C.read(I)}function M(k){b(k.detail)}function b(k){if(!(k&&k.storageArea!==n)){if(k&&k.key==null){y.value=L;return}if(!(k&&k.key!==e)){_();try{(k==null?void 0:k.newValue)!==C.write(y.value)&&(y.value=H(k))}catch(I){m(I)}finally{k?bn(P):P()}}}}}function Sd(e){return Ul("(prefers-color-scheme: dark)",e)}function Dd(e,t,n={}){const{window:r=Ht,...o}=n;let s;const i=Br(()=>r&&"ResizeObserver"in r),l=()=>{s&&(s.disconnect(),s=void 0)},a=N(()=>Array.isArray(e)?e.map(f=>Yt(f)):[Yt(e)]),c=He(a,f=>{if(l(),i.value&&r){s=new ResizeObserver(t);for(const d of f)d&&s.observe(d,o)}},{immediate:!0,flush:"post",deep:!0}),u=()=>{l(),c()};return Fr(u),{isSupported:i,stop:u}}function kd(e,t={width:0,height:0},n={}){const{window:r=Ht,box:o="content-box"}=n,s=N(()=>{var f,d;return(d=(f=Yt(e))==null?void 0:f.namespaceURI)==null?void 0:d.includes("svg")}),i=ue(t.width),l=ue(t.height),{stop:a}=Dd(e,([f])=>{const d=o==="border-box"?f.borderBoxSize:o==="content-box"?f.contentBoxSize:f.devicePixelContentBoxSize;if(r&&s.value){const m=Yt(e);if(m){const g=r.getComputedStyle(m);i.value=Number.parseFloat(g.width),l.value=Number.parseFloat(g.height)}}else if(d){const m=Array.isArray(d)?d:[d];i.value=m.reduce((g,{inlineSize:y})=>g+y,0),l.value=m.reduce((g,{blockSize:y})=>g+y,0)}else i.value=f.contentRect.width,l.value=f.contentRect.height},n);Zo(()=>{const f=Yt(e);f&&(i.value="offsetWidth"in f?f.offsetWidth:t.width,l.value="offsetHeight"in f?f.offsetHeight:t.height)});const c=He(()=>Yt(e),f=>{i.value=f?t.width:0,l.value=f?t.height:0});function u(){a(),c()}return{width:i,height:l,stop:u}}function zd(e={}){const{window:t=Ht,behavior:n="auto"}=e;if(!t)return{x:ue(0),y:ue(0)};const r=ue(t.scrollX),o=ue(t.scrollY),s=N({get(){return r.value},set(l){scrollTo({left:l,behavior:n})}}),i=N({get(){return o.value},set(l){scrollTo({top:l,behavior:n})}});return en(t,"scroll",()=>{r.value=t.scrollX,o.value=t.scrollY},{capture:!1,passive:!0}),{x:s,y:i}}function Vd(e={}){const{window:t=Ht,initialWidth:n=Number.POSITIVE_INFINITY,initialHeight:r=Number.POSITIVE_INFINITY,listenOrientation:o=!0,includeScrollbar:s=!0}=e,i=ue(n),l=ue(r),a=()=>{t&&(s?(i.value=t.innerWidth,l.value=t.innerHeight):(i.value=t.document.documentElement.clientWidth,l.value=t.document.documentElement.clientHeight))};if(a(),Zo(a),en("resize",a,{passive:!0}),o){const c=Ul("(orientation: portrait)");He(c,()=>a())}return{width:i,height:l}}var $d={"/zh/":{backToTop:"返回顶部"},"/":{backToTop:"Back to top"}};const Md=me({name:"BackToTop",setup(e){const t=ht(),n=Fl($d),r=$o(),{height:o}=kd(r),{height:s}=Vd(),{y:i}=zd(),l=N(()=>t.value.backToTop!==!1&&i.value>100),a=N(()=>i.value/(o.value-s.value)*100);return We(()=>{r.value=document.body}),()=>_e(Jn,{name:"back-to-top"},()=>l.value?_e("button",{type:"button",class:"vp-back-to-top-button","aria-label":n.value.backToTop,onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[_e("span",{class:"vp-scroll-progress",role:"progressbar","aria-labelledby":"loadinglabel","aria-valuenow":a.value},_e("svg",_e("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*a.value}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}}))),_e("div",{class:"back-to-top-icon"})]):null)}}),Nd=Ft({rootComponents:[Md]});var Hd={"/zh/":{copy:"复制代码",copied:"已复制"},"/":{copy:"Copy code",copied:"Copied"}},Fd=['.theme-default-content div[class*="language-"] pre'];const so=new Map,Bd=/\b(?:Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini)/i,jd=()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator&&Bd.test(navigator.userAgent),Ud=()=>{const{copy:e}=Pd({legacy:!0}),t=Fl(Hd),n=Zt(),r=i=>{if(!i.hasAttribute("copy-code-registered")){const l=document.createElement("button");l.type="button",l.classList.add("vp-copy-code-button"),l.innerHTML='
',l.setAttribute("aria-label",t.value.copy),l.setAttribute("data-copied",t.value.copied),i.parentElement&&i.parentElement.insertBefore(l,i),i.setAttribute("copy-code-registered","")}},o=()=>{bn().then(()=>setTimeout(()=>{Fd.forEach(i=>{document.querySelectorAll(i).forEach(r)})},800))},s=(i,l,a)=>{let{innerText:c=""}=l;/language-(shellscript|shell|bash|sh|zsh)/.test(i.classList.toString())&&(c=c.replace(/^ *(\$|>) /gm,"")),e(c).then(()=>{a.classList.add("copied"),clearTimeout(so.get(a));const u=setTimeout(()=>{a.classList.remove("copied"),a.blur(),so.delete(a)},2e3);so.set(a,u)})};We(()=>{const i=!jd()||!1;i&&o(),en("click",l=>{const a=l.target;if(a.matches('div[class*="language-"] > button.copy')){const c=a.parentElement,u=a.nextElementSibling;u&&s(c,u,a)}else if(a.matches('div[class*="language-"] div.vp-copy-icon')){const c=a.parentElement,u=c.parentElement,f=c.nextElementSibling;f&&s(u,f,c)}}),He(()=>n.value.path,()=>{i&&o()})})},Wd=Ft({setup:()=>{Ud()}}),Kd=_e("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[_e("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),_e("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),qd=me({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=Qn(),n=N(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>_e("span",[Kd,_e("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}});var Gd={"/":{openInNewWindow:"open in new window"},"/zh/":{openInNewWindow:"在新窗口打开"}};const Yd=Gd,Jd=Ft({enhance({app:e}){e.component("ExternalLinkIcon",_e(qd,{locales:Yd}))}});/*! medium-zoom 1.1.0 | MIT License | https://github.com/francoischalifour/medium-zoom */var Wt=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},r=window.Promise||function(w){function $(){}w($,$)},o=function(w){var $=w.target;if($===k){g();return}_.indexOf($)!==-1&&y({target:$})},s=function(){if(!(F||!b.original)){var w=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(H-w)>M.scrollOffset&&setTimeout(g,150)}},i=function(w){var $=w.key||w.keyCode;($==="Escape"||$==="Esc"||$===27)&&g()},l=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},$=w;if(w.background&&(k.style.background=w.background),w.container&&w.container instanceof Object&&($.container=Wt({},M.container,w.container)),w.template){var re=pr(w.template)?w.template:document.querySelector(w.template);$.template=re}return M=Wt({},M,$),_.forEach(function(ie){ie.dispatchEvent(sn("medium-zoom:update",{detail:{zoom:I}}))}),I},a=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Wt({},M,w))},c=function(){for(var w=arguments.length,$=Array(w),re=0;re0?$.reduce(function(D,X){return[].concat(D,di(X))},[]):_;return ie.forEach(function(D){D.classList.remove("medium-zoom-image"),D.dispatchEvent(sn("medium-zoom:detach",{detail:{zoom:I}}))}),_=_.filter(function(D){return ie.indexOf(D)===-1}),I},f=function(w,$){var re=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return _.forEach(function(ie){ie.addEventListener("medium-zoom:"+w,$,re)}),P.push({type:"medium-zoom:"+w,listener:$,options:re}),I},d=function(w,$){var re=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return _.forEach(function(ie){ie.removeEventListener("medium-zoom:"+w,$,re)}),P=P.filter(function(ie){return!(ie.type==="medium-zoom:"+w&&ie.listener.toString()===$.toString())}),I},m=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},$=w.target,re=function(){var D={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},X=void 0,q=void 0;if(M.container)if(M.container instanceof Object)D=Wt({},D,M.container),X=D.width-D.left-D.right-M.margin*2,q=D.height-D.top-D.bottom-M.margin*2;else{var De=pr(M.container)?M.container:document.querySelector(M.container),$e=De.getBoundingClientRect(),Ke=$e.width,Fe=$e.height,yt=$e.left,Lt=$e.top;D=Wt({},D,{width:Ke,height:Fe,left:yt,top:Lt})}X=X||D.width-M.margin*2,q=q||D.height-M.margin*2;var at=b.zoomedHd||b.original,ke=fi(at)?X:at.naturalWidth||X,R=fi(at)?q:at.naturalHeight||q,K=at.getBoundingClientRect(),j=K.top,J=K.left,ce=K.width,ge=K.height,h=Math.min(Math.max(ce,ke),X)/ce,p=Math.min(Math.max(ge,R),q)/ge,E=Math.min(h,p),O=(-J+(X-ce)/2+M.margin+D.left)/E,T=(-j+(q-ge)/2+M.margin+D.top)/E,z="scale("+E+") translate3d("+O+"px, "+T+"px, 0)";b.zoomed.style.transform=z,b.zoomedHd&&(b.zoomedHd.style.transform=z)};return new r(function(ie){if($&&_.indexOf($)===-1){ie(I);return}var D=function Ke(){F=!1,b.zoomed.removeEventListener("transitionend",Ke),b.original.dispatchEvent(sn("medium-zoom:opened",{detail:{zoom:I}})),ie(I)};if(b.zoomed){ie(I);return}if($)b.original=$;else if(_.length>0){var X=_;b.original=X[0]}else{ie(I);return}if(b.original.dispatchEvent(sn("medium-zoom:open",{detail:{zoom:I}})),H=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,F=!0,b.zoomed=Zd(b.original),document.body.appendChild(k),M.template){var q=pr(M.template)?M.template:document.querySelector(M.template);b.template=document.createElement("div"),b.template.appendChild(q.content.cloneNode(!0)),document.body.appendChild(b.template)}if(b.original.parentElement&&b.original.parentElement.tagName==="PICTURE"&&b.original.currentSrc&&(b.zoomed.src=b.original.currentSrc),document.body.appendChild(b.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),b.original.classList.add("medium-zoom-image--hidden"),b.zoomed.classList.add("medium-zoom-image--opened"),b.zoomed.addEventListener("click",g),b.zoomed.addEventListener("transitionend",D),b.original.getAttribute("data-zoom-src")){b.zoomedHd=b.zoomed.cloneNode(),b.zoomedHd.removeAttribute("srcset"),b.zoomedHd.removeAttribute("sizes"),b.zoomedHd.removeAttribute("loading"),b.zoomedHd.src=b.zoomed.getAttribute("data-zoom-src"),b.zoomedHd.onerror=function(){clearInterval(De),console.warn("Unable to reach the zoom image target "+b.zoomedHd.src),b.zoomedHd=null,re()};var De=setInterval(function(){b.zoomedHd.complete&&(clearInterval(De),b.zoomedHd.classList.add("medium-zoom-image--opened"),b.zoomedHd.addEventListener("click",g),document.body.appendChild(b.zoomedHd),re())},10)}else if(b.original.hasAttribute("srcset")){b.zoomedHd=b.zoomed.cloneNode(),b.zoomedHd.removeAttribute("sizes"),b.zoomedHd.removeAttribute("loading");var $e=b.zoomedHd.addEventListener("load",function(){b.zoomedHd.removeEventListener("load",$e),b.zoomedHd.classList.add("medium-zoom-image--opened"),b.zoomedHd.addEventListener("click",g),document.body.appendChild(b.zoomedHd),re()})}else re()})},g=function(){return new r(function(w){if(F||!b.original){w(I);return}var $=function re(){b.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(b.zoomed),b.zoomedHd&&document.body.removeChild(b.zoomedHd),document.body.removeChild(k),b.zoomed.classList.remove("medium-zoom-image--opened"),b.template&&document.body.removeChild(b.template),F=!1,b.zoomed.removeEventListener("transitionend",re),b.original.dispatchEvent(sn("medium-zoom:closed",{detail:{zoom:I}})),b.original=null,b.zoomed=null,b.zoomedHd=null,b.template=null,w(I)};F=!0,document.body.classList.remove("medium-zoom--opened"),b.zoomed.style.transform="",b.zoomedHd&&(b.zoomedHd.style.transform=""),b.template&&(b.template.style.transition="opacity 150ms",b.template.style.opacity=0),b.original.dispatchEvent(sn("medium-zoom:close",{detail:{zoom:I}})),b.zoomed.addEventListener("transitionend",$)})},y=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},$=w.target;return b.original?g():m({target:$})},L=function(){return M},A=function(){return _},C=function(){return b.original},_=[],P=[],F=!1,H=0,M=n,b={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?M=t:(t||typeof t=="string")&&c(t),M=Wt({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},M);var k=Qd(M.background);document.addEventListener("click",o),document.addEventListener("keyup",i),document.addEventListener("scroll",s),window.addEventListener("resize",g);var I={open:m,close:g,toggle:y,update:l,clone:a,attach:c,detach:u,on:f,off:d,getOptions:L,getImages:A,getZoomedImage:C};return I};function th(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(!e||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 nh=".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}";th(nh);const rh=eh,oh=Symbol("mediumZoom");var sh={};const ih=".theme-default-content > img, .theme-default-content :not(a) > img",lh=sh,ah=300,ch=Ft({enhance({app:e,router:t}){const n=rh(lh);n.refresh=(r=ih)=>{n.detach(),n.attach(r)},e.provide(oh,n),t.afterEach(()=>{setTimeout(()=>n.refresh(),ah)})}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const fe={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=fe.isStarted();e=io(e,fe.settings.minimum,1),fe.status=e===1?null:e;const n=fe.render(!t),r=n.querySelector(fe.settings.barSelector),o=fe.settings.speed,s=fe.settings.easing;return n.offsetWidth,uh(i=>{fr(r,{transform:"translate3d("+hi(e)+"%,0,0)",transition:"all "+o+"ms "+s}),e===1?(fr(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){fr(n,{transition:"all "+o+"ms linear",opacity:"0"}),setTimeout(function(){fe.remove(),i()},o)},o)):setTimeout(()=>i(),o)}),fe},isStarted:()=>typeof fe.status=="number",start:()=>{fe.status||fe.set(0);const e=()=>{setTimeout(()=>{fe.status&&(fe.trickle(),e())},fe.settings.trickleSpeed)};return fe.settings.trickle&&e(),fe},done:e=>!e&&!fe.status?fe:fe.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=fe.status;return t?(typeof e!="number"&&(e=(1-t)*io(Math.random()*t,.1,.95)),t=io(t+e,0,.994),fe.set(t)):fe.start()},trickle:()=>fe.inc(Math.random()*fe.settings.trickleRate),render:e=>{if(fe.isRendered())return document.getElementById("nprogress");pi(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=fe.settings.template;const n=t.querySelector(fe.settings.barSelector),r=e?"-100":hi(fe.status||0),o=document.querySelector(fe.settings.parent);return fr(n,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),o!==document.body&&pi(o,"nprogress-custom-parent"),o==null||o.appendChild(t),t},remove:()=>{mi(document.documentElement,"nprogress-busy"),mi(document.querySelector(fe.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&fh(e)},isRendered:()=>!!document.getElementById("nprogress")},io=(e,t,n)=>en?n:e,hi=e=>(-1+e)*100,uh=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),fr=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(l,a){return a.toUpperCase()})}function r(i){const l=document.body.style;if(i in l)return i;let a=e.length;const c=i.charAt(0).toUpperCase()+i.slice(1);let u;for(;a--;)if(u=e[a]+c,u in l)return u;return i}function o(i){return i=n(i),t[i]??(t[i]=r(i))}function s(i,l,a){l=o(l),i.style[l]=a}return function(i,l){for(const a in l){const c=l[a];c!==void 0&&Object.prototype.hasOwnProperty.call(l,a)&&s(i,a,c)}}}(),Kl=(e,t)=>(typeof e=="string"?e:es(e)).indexOf(" "+t+" ")>=0,pi=(e,t)=>{const n=es(e),r=n+t;Kl(n,t)||(e.className=r.substring(1))},mi=(e,t)=>{const n=es(e);if(!Kl(e,t))return;const r=n.replace(" "+t+" "," ");e.className=r.substring(1,r.length-1)},es=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),fh=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)},dh=()=>{We(()=>{const e=Mt(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||fe.start()}),e.afterEach(n=>{t.add(n.path),fe.done()})})},hh=Ft({setup(){dh()}}),ph=JSON.parse(`{"logo":"/images/hero.png","repo":"vuepress/docs","docsDir":"docs","locales":{"/":{"navbar":[{"text":"Themes","children":[{"text":"Default Theme","link":"/themes/default/"}]},{"text":"Plugins","children":[{"text":"Common Features","children":["/plugins/back-to-top","/plugins/container","/plugins/copy-code","/plugins/copyright","/plugins/external-link-icon","/plugins/google-analytics","/plugins/medium-zoom","/plugins/nprogress","/plugins/redirect","/plugins/register-components"]},{"text":"Search","children":["/plugins/docsearch","/plugins/search"]},{"text":"Blogging","children":["/plugins/feed/"]},{"text":"PWA","children":["/plugins/pwa","/plugins/pwa-popup","/plugins/remove-pwa"]},{"text":"SEO","children":["/plugins/seo/","/plugins/sitemap/"]},{"text":"Syntax Highlighting","children":["/plugins/prismjs","/plugins/shiki"]},{"text":"Theme Development","children":["/plugins/active-header-links","/plugins/git","/plugins/palette","/plugins/reading-time","/plugins/rtl","/plugins/theme-data","/plugins/toc"]}]}],"sidebar":{"/plugins/":[{"text":"Common Features","children":["/plugins/back-to-top","/plugins/container","/plugins/copy-code","/plugins/copyright","/plugins/external-link-icon","/plugins/google-analytics","/plugins/medium-zoom","/plugins/nprogress","/plugins/redirect","/plugins/register-components"]},{"text":"Content Search","children":["/plugins/docsearch","/plugins/search"]},{"text":"Blogging","children":[{"text":"Feed","link":"/plugins/feed/","children":["/plugins/feed/guide","/plugins/feed/config","/plugins/feed/frontmatter","/plugins/feed/channel","/plugins/feed/getter"]}]},{"text":"PWA","children":["/plugins/pwa","/plugins/pwa-popup","/plugins/remove-pwa"]},{"text":"SEO","children":[{"text":"SEO","link":"/plugins/seo/","children":["/plugins/seo/guide","/plugins/seo/config"]},{"text":"Sitemap","link":"/plugins/sitemap/","children":["/plugins/sitemap/guide","/plugins/sitemap/config","/plugins/sitemap/frontmatter"]}]},{"text":"Syntax Highlighting","children":["/plugins/prismjs","/plugins/shiki"]},{"text":"Theme Development","children":["/plugins/active-header-links","/plugins/git","/plugins/palette","/plugins/reading-time","/plugins/rtl","/plugins/theme-data","/plugins/toc"]}],"/themes/":[{"text":"Default Theme","children":["/themes/default/","/themes/default/config","/themes/default/plugin","/themes/default/locale","/themes/default/frontmatter","/themes/default/components","/themes/default/markdown","/themes/default/styles","/themes/default/extending"]}]},"editLinkText":"Edit this page on GitHub","selectLanguageName":"English"},"/zh/":{"navbar":[{"text":"主题","children":[{"text":"默认主题","link":"/zh/themes/default/"}]},{"text":"插件","children":[{"text":"常用功能","children":["/zh/plugins/back-to-top","/zh/plugins/container","/zh/plugins/copy-code","/zh/plugins/copyright","/zh/plugins/external-link-icon","/zh/plugins/google-analytics","/zh/plugins/medium-zoom","/zh/plugins/nprogress","/zh/plugins/redirect","/zh/plugins/register-components"]},{"text":"搜索","children":["/zh/plugins/docsearch","/zh/plugins/search"]},{"text":"博客","children":["/plugins/feed/"]},{"text":"PWA","children":["/zh/plugins/pwa","/zh/plugins/pwa-popup","/zh/plugins/remove-pwa"]},{"text":"搜索引擎增强","children":["/zh/plugins/seo/","/zh/plugins/sitemap/"]},{"text":"语法高亮","children":["/zh/plugins/prismjs","/zh/plugins/shiki"]},{"text":"主题开发","children":["/zh/plugins/active-header-links","/zh/plugins/git","/zh/plugins/palette","/zh/plugins/reading-time","/zh/plugins/rtl","/zh/plugins/theme-data","/zh/plugins/toc"]}]}],"selectLanguageName":"简体中文","selectLanguageText":"选择语言","selectLanguageAriaLabel":"选择语言","sidebar":{"/zh/plugins/":[{"text":"常用功能","children":["/zh/plugins/back-to-top","/zh/plugins/container","/zh/plugins/copy-code","/zh/plugins/copyright","/zh/plugins/external-link-icon","/zh/plugins/google-analytics","/zh/plugins/medium-zoom","/zh/plugins/nprogress","/zh/plugins/redirect","/zh/plugins/register-components"]},{"text":"搜索","children":["/zh/plugins/docsearch","/zh/plugins/search"]},{"text":"博客","children":[{"text":"Feed","link":"/zh/plugins/feed/","children":["/zh/plugins/feed/guide","/zh/plugins/feed/config","/zh/themes/default/plugin","/zh/plugins/feed/frontmatter","/zh/plugins/feed/channel","/zh/plugins/feed/getter"]}]},{"text":"PWA","children":["/zh/plugins/pwa","/zh/plugins/pwa-popup","/zh/plugins/remove-pwa"]},{"text":"搜索引擎增强","children":[{"text":"搜索引擎增强","link":"/zh/plugins/seo/","children":["/zh/plugins/seo/guide","/zh/plugins/seo/config"]},{"text":"站点地图","link":"/zh/plugins/sitemap/","children":["/zh/plugins/sitemap/guide","/zh/plugins/sitemap/config","/zh/plugins/sitemap/frontmatter"]}]},{"text":"语法高亮","children":["/zh/plugins/prismjs","/zh/plugins/shiki"]},{"text":"主题开发","children":["/zh/plugins/active-header-links","/zh/plugins/git","/zh/plugins/palette","/zh/plugins/reading-time","/zh/plugins/rtl","/zh/plugins/theme-data","/zh/plugins/toc"]}],"/zh/themes/":[{"text":"默认主题","children":["/zh/themes/default/","/zh/themes/default/config","/zh/themes/default/plugin","/zh/themes/default/locale","/zh/themes/default/frontmatter","/zh/themes/default/components","/zh/themes/default/markdown","/zh/themes/default/styles","/zh/themes/default/extending"]}]},"editLinkText":"在 GitHub 上编辑此页","lastUpdatedText":"上次更新","contributorsText":"贡献者","tip":"提示","warning":"注意","danger":"警告","notFound":["这里什么都没有","我们怎么到这来了?","这是一个 404 页面","看起来我们进入了错误的链接"],"backToHome":"返回首页","openInNewWindow":"在新窗口打开","toggleColorMode":"切换颜色模式","toggleSidebar":"切换侧边栏"}},"colorMode":"auto","colorModeSwitch":true,"navbar":[],"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","sidebar":"auto","sidebarDepth":2,"editLink":true,"editLinkText":"Edit this page","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),mh=ue(ph),ql=()=>mh,Gl=Symbol(""),vh=()=>{const e=Pe(Gl);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},_h=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},gh=Ft({enhance({app:e}){const t=ql(),n=e._context.provides[Ko],r=N(()=>_h(t.value,n.value));e.provide(Gl,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}})}}),bh=me({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,n)=>(W(),Z("span",{class:Ge(["badge",e.type]),style:jn({verticalAlign:e.vertical})},[Ee(t.$slots,"default",{},()=>[$t(Ie(e.text),1)])],6))}}),Ae=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n},Eh=Ae(bh,[["__file","Badge.vue"]]),yh=me({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const n=ue([]),r=ue(-1),o=Wl("vuepress-code-group",{}),s=N(()=>n.value.map(c=>c.innerText).join(","));We(()=>{He(()=>o.value[s.value],(c=-1)=>{r.value!==c&&(r.value=c)},{immediate:!0}),He(r,c=>{o.value[s.value]!==c&&(o.value[s.value]=c)})});const i=(c=r.value)=>{c{c>0?r.value=c-1:r.value=n.value.length-1,n.value[r.value].focus()},a=(c,u)=>{c.key===" "||c.key==="Enter"?(c.preventDefault(),r.value=u):c.key==="ArrowRight"?(c.preventDefault(),i(u)):c.key==="ArrowLeft"&&(c.preventDefault(),l(u))};return()=>{var u;const c=(((u=t.default)==null?void 0:u.call(t))||[]).filter(f=>f.type.name==="CodeGroupItem").map(f=>(f.props===null&&(f.props={}),f));return c.length===0?null:(r.value<0||r.value>c.length-1?(r.value=c.findIndex(f=>f.props.active===""||f.props.active===!0),r.value===-1&&(r.value=0)):c.forEach((f,d)=>{f.props.active=d===r.value}),_e("div",{class:"code-group"},[_e("div",{class:"code-group__nav",role:"tablist"},c.map((f,d)=>{const m=d===r.value;return _e("button",{ref:g=>{g&&(n.value[d]=g)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":m},role:"tab",ariaSelected:m,onClick:()=>r.value=d,onKeydown:g=>a(g,d)},f.props.title)})),c]))}}}),Lh=me({name:"CodeGroupItem",__name:"CodeGroupItem",props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,n)=>(W(),Z("div",{class:Ge(["code-group-item",{"code-group-item__active":e.active}]),role:"tabpanel"},[Ee(t.$slots,"default")],2))}}),wh=Ae(Lh,[["__file","CodeGroupItem.vue"]]),Th=()=>ql(),Ne=()=>vh(),Yl=Symbol(""),ts=()=>{const e=Pe(Yl);if(!e)throw new Error("useDarkMode() is called without provider.");return e},Ah=()=>{const e=Ne(),t=Sd(),n=Wl("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"}});Qt(Yl,r),Ph(r)},Ph=e=>{const t=(n=e.value)=>{const r=window==null?void 0:window.document.querySelector("html");r==null||r.classList.toggle("dark",n)};We(()=>{He(e,t,{immediate:!0})}),zr(()=>t())};let lo=null,An=null;const Rh={wait:()=>lo,pending:()=>{lo=new Promise(e=>An=e)},resolve:()=>{An==null||An(),lo=null,An=null}},Jl=()=>Rh,Xl=(e,...t)=>{const n=e.resolve(...t),r=n.matched[n.matched.length-1];if(!(r!=null&&r.redirect))return n;const{redirect:o}=r,s=Wu(o)?o(n):o,i=dt(s)?{path:s}:s;return Xl(e,{hash:n.hash,query:n.query,params:n.params,...i})},ns=(e,t)=>{const n=Xl(e,encodeURI(t));return{text:n.meta.title||t,link:n.name==="404"?t:n.fullPath}},vi=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),Oh=(e,t)=>{if(t.hash===e)return!0;const n=vi(t.path),r=vi(e);return n===r},Ql=(e,t)=>e.link&&Oh(e.link,t)?!0:e.children?e.children.some(n=>Ql(n,t)):!1,Zl=e=>!Xn(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,Ch={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"},Ih=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const n=Zl(e);return n!==null?Ch[n]:null},xh=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const s=Ih({docsRepo:e,editLinkPattern:o});return s?s.replace(/:repo/,Xn(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,El(`${bl(n)}/${r}`)):null},ea=Symbol("sidebarItems"),rs=()=>{const e=Pe(ea);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},Sh=()=>{const e=Ne(),t=ht(),n=Zt(),r=En(),o=Mt(),s=N(()=>Dh(t.value,e.value,n.value,o,r.path));Qt(ea,s)},Dh=(e,t,n,r,o)=>{const s=e.sidebar??t.sidebar??"auto",i=e.sidebarDepth??t.sidebarDepth??2;return e.home||s===!1?[]:s==="auto"?ta(n,i):Array.isArray(s)?na(n,r,o,s,i):Wo(s)?zh(n,r,o,s,i):[]},kh=(e,t)=>({text:e.title,link:e.link,children:os(e.children,t)}),os=(e,t)=>t>0?e.map(n=>kh(n,t-1)):[],ta=(e,t)=>[{text:e.title,children:os(e.headers,t)}],na=(e,t,n,r,o)=>{const s=i=>{var a;let l;if(dt(i)?l=ns(t,i):l=i,l.children)return{...l,children:l.children.map(c=>s(c))};if(l.link===n){const c=((a=e.headers[0])==null?void 0:a.level)===1?e.headers[0].children:e.headers;return{...l,children:os(c,o)}}return l};return r.map(i=>s(i))},zh=(e,t,n,r,o)=>{const s=yl(r,n),i=r[s]??[];return i==="heading"?ta(e,o):na(e,t,n,i,o)},Vh="719px",$h={mobile:Vh};var Hn;(function(e){e.MOBILE="mobile"})(Hn||(Hn={}));var gi;const Mh={[Hn.MOBILE]:Number.parseInt((gi=$h.mobile)==null?void 0:gi.replace("px",""),10)},ra=(e,t)=>{const n=Mh[e];Number.isInteger(n)&&We(()=>{t(n),window.addEventListener("resize",()=>t(n),!1),window.addEventListener("orientationchange",()=>t(n),!1)})},Nh={},Hh={class:"theme-default-content"};function Fh(e,t){const n=bt("Content");return W(),Z("div",Hh,[oe(n)])}const Bh=Ae(Nh,[["render",Fh],["__file","HomeContent.vue"]]),jh={key:0,class:"features"},Uh=me({__name:"HomeFeatures",setup(e){const t=ht(),n=N(()=>Array.isArray(t.value.features)?t.value.features:[]);return(r,o)=>n.value.length?(W(),Z("div",jh,[(W(!0),Z(ye,null,zt(n.value,s=>(W(),Z("div",{key:s.title,class:"feature"},[he("h2",null,Ie(s.title),1),he("p",null,Ie(s.details),1)]))),128))])):Oe("",!0)}}),Wh=Ae(Uh,[["__file","HomeFeatures.vue"]]),Kh=["innerHTML"],qh=["textContent"],Gh=me({__name:"HomeFooter",setup(e){const t=ht(),n=N(()=>t.value.footer),r=N(()=>t.value.footerHtml);return(o,s)=>n.value?(W(),Z(ye,{key:0},[r.value?(W(),Z("div",{key:0,class:"footer",innerHTML:n.value},null,8,Kh)):(W(),Z("div",{key:1,class:"footer",textContent:Ie(n.value)},null,8,qh))],64)):Oe("",!0)}}),Yh=Ae(Gh,[["__file","HomeFooter.vue"]]),Jh=["href","rel","target","aria-label"],Xh=me({inheritAttrs:!1,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(e){const t=e,n=En(),r=Ol(),{item:o}=Mo(t),s=N(()=>Xn(o.value.link)),i=N(()=>!s.value&&Uu(o.value.link)),l=N(()=>{if(!i.value){if(o.value.target)return o.value.target;if(s.value)return"_blank"}}),a=N(()=>l.value==="_blank"),c=N(()=>!s.value&&!i.value&&!a.value),u=N(()=>{if(!i.value){if(o.value.rel)return o.value.rel;if(a.value)return"noopener noreferrer"}}),f=N(()=>o.value.ariaLabel||o.value.text),d=N(()=>{const y=Object.keys(r.value.locales);return y.length?!y.some(L=>L===o.value.link):o.value.link!=="/"}),m=N(()=>d.value?n.path.startsWith(o.value.link):!1),g=N(()=>c.value?o.value.activeMatch?new RegExp(o.value.activeMatch).test(n.path):m.value:!1);return(y,L)=>{const A=bt("RouterLink"),C=bt("AutoLinkExternalIcon");return c.value?(W(),Re(A,bo({key:0,class:{"router-link-active":g.value},to:te(o).link,"aria-label":f.value},y.$attrs),{default:Me(()=>[Ee(y.$slots,"before"),$t(" "+Ie(te(o).text)+" ",1),Ee(y.$slots,"after")]),_:3},16,["class","to","aria-label"])):(W(),Z("a",bo({key:1,class:"external-link",href:te(o).link,rel:u.value,target:l.value,"aria-label":f.value},y.$attrs),[Ee(y.$slots,"before"),$t(" "+Ie(te(o).text)+" ",1),a.value?(W(),Re(C,{key:0})):Oe("",!0),Ee(y.$slots,"after")],16,Jh))}}}),gt=Ae(Xh,[["__file","AutoLink.vue"]]),Qh={class:"hero"},Zh={key:0,id:"main-title"},ep={key:1,class:"description"},tp={key:2,class:"actions"},np=me({__name:"HomeHero",setup(e){const t=ht(),n=qo(),r=ts(),o=N(()=>r.value&&t.value.heroImageDark!==void 0?t.value.heroImageDark:t.value.heroImage),s=N(()=>t.value.heroAlt||l.value||"hero"),i=N(()=>t.value.heroHeight||280),l=N(()=>t.value.heroText===null?null:t.value.heroText||n.value.title||"Hello"),a=N(()=>t.value.tagline===null?null:t.value.tagline||n.value.description||"Welcome to your VuePress site"),c=N(()=>Array.isArray(t.value.actions)?t.value.actions.map(({text:f,link:d,type:m="primary"})=>({text:f,link:d,type:m})):[]),u=()=>{if(!o.value)return null;const f=_e("img",{src:Yo(o.value),alt:s.value,height:i.value});return t.value.heroImageDark===void 0?f:_e(Go,()=>f)};return(f,d)=>(W(),Z("header",Qh,[oe(u),l.value?(W(),Z("h1",Zh,Ie(l.value),1)):Oe("",!0),a.value?(W(),Z("p",ep,Ie(a.value),1)):Oe("",!0),c.value.length?(W(),Z("p",tp,[(W(!0),Z(ye,null,zt(c.value,m=>(W(),Re(gt,{key:m.text,class:Ge(["action-button",[m.type]]),item:m},null,8,["class","item"]))),128))])):Oe("",!0)]))}}),rp=Ae(np,[["__file","HomeHero.vue"]]),op={class:"home"},sp=me({__name:"Home",setup(e){return(t,n)=>(W(),Z("main",op,[oe(rp),oe(Wh),oe(Bh),oe(Yh)]))}}),ip=Ae(sp,[["__file","Home.vue"]]),lp=["aria-hidden"],ap=me({__name:"NavbarBrand",setup(e){const t=Qn(),n=qo(),r=Ne(),o=ts(),s=N(()=>r.value.home||t.value),i=N(()=>n.value.title),l=N(()=>o.value&&r.value.logoDark!==void 0?r.value.logoDark:r.value.logo),a=N(()=>r.value.logoAlt??i.value),c=N(()=>i.value.toLocaleUpperCase().trim()===a.value.toLocaleUpperCase().trim()),u=()=>{if(!l.value)return null;const f=_e("img",{class:"logo",src:Yo(l.value),alt:a.value});return r.value.logoDark===void 0?f:_e(Go,()=>f)};return(f,d)=>{const m=bt("RouterLink");return W(),Re(m,{to:s.value},{default:Me(()=>[oe(u),i.value?(W(),Z("span",{key:0,class:Ge(["site-name",{"can-hide":l.value}]),"aria-hidden":c.value},Ie(i.value),11,lp)):Oe("",!0)]),_:1},8,["to"])}}}),cp=Ae(ap,[["__file","NavbarBrand.vue"]]),up=me({__name:"DropdownTransition",setup(e){const t=r=>{r.style.height=r.scrollHeight+"px"},n=r=>{r.style.height=""};return(r,o)=>(W(),Re(Jn,{name:"dropdown",onEnter:t,onAfterEnter:n,onBeforeLeave:t},{default:Me(()=>[Ee(r.$slots,"default")]),_:3}))}}),oa=Ae(up,[["__file","DropdownTransition.vue"]]),fp=["aria-label"],dp={class:"title"},hp=he("span",{class:"arrow down"},null,-1),pp=["aria-label"],mp={class:"title"},vp={class:"navbar-dropdown"},_p={class:"navbar-dropdown-subtitle"},gp={key:1},bp={class:"navbar-dropdown-subitem-wrapper"},Ep=me({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(e){const t=e,{item:n}=Mo(t),r=N(()=>n.value.ariaLabel||n.value.text),o=ue(!1),s=En();He(()=>s.path,()=>{o.value=!1});const i=a=>{a.detail===0?o.value=!o.value:o.value=!1},l=(a,c)=>c[c.length-1]===a;return(a,c)=>(W(),Z("div",{class:Ge(["navbar-dropdown-wrapper",{open:o.value}])},[he("button",{class:"navbar-dropdown-title",type:"button","aria-label":r.value,onClick:i},[he("span",dp,Ie(te(n).text),1),hp],8,fp),he("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":r.value,onClick:c[0]||(c[0]=u=>o.value=!o.value)},[he("span",mp,Ie(te(n).text),1),he("span",{class:Ge(["arrow",o.value?"down":"right"])},null,2)],8,pp),oe(oa,null,{default:Me(()=>[Er(he("ul",vp,[(W(!0),Z(ye,null,zt(te(n).children,u=>(W(),Z("li",{key:u.text,class:"navbar-dropdown-item"},[u.children?(W(),Z(ye,{key:0},[he("h4",_p,[u.link?(W(),Re(gt,{key:0,item:u,onFocusout:f=>l(u,te(n).children)&&u.children.length===0&&(o.value=!1)},null,8,["item","onFocusout"])):(W(),Z("span",gp,Ie(u.text),1))]),he("ul",bp,[(W(!0),Z(ye,null,zt(u.children,f=>(W(),Z("li",{key:f.link,class:"navbar-dropdown-subitem"},[oe(gt,{item:f,onFocusout:d=>l(f,u.children)&&l(u,te(n).children)&&(o.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(W(),Re(gt,{key:1,item:u,onFocusout:f=>l(u,te(n).children)&&(o.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[Pr,o.value]])]),_:1})],2))}}),yp=Ae(Ep,[["__file","NavbarDropdown.vue"]]),Lp=["aria-label"],wp=me({__name:"NavbarItems",setup(e){const t=()=>{const f=Mt(),d=Qn(),m=Ol(),g=qo(),y=Th(),L=Ne();return N(()=>{const A=Object.keys(m.value.locales);if(A.length<2)return[];const C=f.currentRoute.value.path,_=f.currentRoute.value.fullPath;return[{text:`${L.value.selectLanguageText}`,ariaLabel:`${L.value.selectLanguageAriaLabel??L.value.selectLanguageText}`,children:A.map(F=>{var Y,w;const H=((Y=m.value.locales)==null?void 0:Y[F])??{},M=((w=y.value.locales)==null?void 0:w[F])??{},b=`${H.lang}`,k=M.selectLanguageName??b;let I;if(b===g.value.lang)I=_;else{const $=C.replace(d.value,F);f.getRoutes().some(re=>re.path===$)?I=_.replace(C,$):I=M.home??F}return{text:k,link:I}})}]})},n=()=>{const f=Ne(),d=N(()=>f.value.repo),m=N(()=>d.value?Zl(d.value):null),g=N(()=>d.value&&!Xn(d.value)?`https://github.com/${d.value}`:d.value),y=N(()=>g.value?f.value.repoLabel?f.value.repoLabel:m.value===null?"Source":m.value:null);return N(()=>!g.value||!y.value?[]:[{text:y.value,link:g.value}])},r=(f,d)=>dt(d)?ns(f,d):d.children?{...d,children:d.children.map(m=>r(f,m))}:d,o=()=>{const f=Mt(),d=Ne();return N(()=>(d.value.navbar||[]).map(m=>r(f,m)))},s=ue(!1),i=o(),l=t(),a=n(),c=N(()=>[...i.value,...l.value,...a.value]);ra(Hn.MOBILE,f=>{window.innerWidthNe().value.navbarLabel??"site navigation");return(f,d)=>c.value.length?(W(),Z("nav",{key:0,class:"navbar-items","aria-label":u.value},[(W(!0),Z(ye,null,zt(c.value,m=>(W(),Z("div",{key:m.text,class:"navbar-item"},[m.children?(W(),Re(yp,{key:0,item:m,class:Ge(s.value?"mobile":"")},null,8,["item","class"])):(W(),Re(gt,{key:1,item:m},null,8,["item"]))]))),128))],8,Lp)):Oe("",!0)}}),sa=Ae(wp,[["__file","NavbarItems.vue"]]),Tp=["title"],Ap={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Pp=Gc('',9),Rp=[Pp],Op={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Cp=he("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),Ip=[Cp],xp=me({__name:"ToggleColorModeButton",setup(e){const t=Ne(),n=ts(),r=()=>{n.value=!n.value};return(o,s)=>(W(),Z("button",{class:"toggle-color-mode-button",title:te(t).toggleColorMode,onClick:r},[Er((W(),Z("svg",Ap,Rp,512)),[[Pr,!te(n)]]),Er((W(),Z("svg",Op,Ip,512)),[[Pr,te(n)]])],8,Tp))}}),Sp=Ae(xp,[["__file","ToggleColorModeButton.vue"]]),Dp=["title"],kp=he("div",{class:"icon","aria-hidden":"true"},[he("span"),he("span"),he("span")],-1),zp=[kp],Vp=me({__name:"ToggleSidebarButton",emits:["toggle"],setup(e){const t=Ne();return(n,r)=>(W(),Z("div",{class:"toggle-sidebar-button",title:te(t).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:r[0]||(r[0]=o=>n.$emit("toggle"))},zp,8,Dp))}}),$p=Ae(Vp,[["__file","ToggleSidebarButton.vue"]]),Mp=me({__name:"Navbar",emits:["toggle-sidebar"],setup(e){const t=Ne(),n=ue(null),r=ue(null),o=ue(0),s=N(()=>o.value?{maxWidth:o.value+"px"}:{});ra(Hn.MOBILE,l=>{var c;const a=i(n.value,"paddingLeft")+i(n.value,"paddingRight");window.innerWidth{const c=bt("NavbarSearch");return W(),Z("header",{ref_key:"navbar",ref:n,class:"navbar"},[oe($p,{onToggle:a[0]||(a[0]=u=>l.$emit("toggle-sidebar"))}),he("span",{ref_key:"navbarBrand",ref:r},[oe(cp)],512),he("div",{class:"navbar-items-wrapper",style:jn(s.value)},[Ee(l.$slots,"before"),oe(sa,{class:"can-hide"}),Ee(l.$slots,"after"),te(t).colorModeSwitch?(W(),Re(Sp,{key:0})):Oe("",!0),oe(c)],4)],512)}}}),Np=Ae(Mp,[["__file","Navbar.vue"]]),Hp={class:"page-meta"},Fp={key:0,class:"meta-item edit-link"},Bp={key:1,class:"meta-item last-updated"},jp={class:"meta-item-label"},Up={class:"meta-item-info"},Wp={key:2,class:"meta-item contributors"},Kp={class:"meta-item-label"},qp={class:"meta-item-info"},Gp=["title"],Yp=me({__name:"PageMeta",setup(e){const t=()=>{const a=Ne(),c=Zt(),u=ht();return N(()=>{if(!(u.value.editLink??a.value.editLink??!0))return null;const{repo:d,docsRepo:m=d,docsBranch:g="main",docsDir:y="",editLinkText:L}=a.value;if(!m)return null;const A=xh({docsRepo:m,docsBranch:g,docsDir:y,filePathRelative:c.value.filePathRelative,editLinkPattern:u.value.editLinkPattern??a.value.editLinkPattern});return A?{text:L??"Edit this page",link:A}:null})},n=()=>{const a=Ne(),c=Zt(),u=ht();return N(()=>{var m,g;return!(u.value.lastUpdated??a.value.lastUpdated??!0)||!((m=c.value.git)!=null&&m.updatedTime)?null:new Date((g=c.value.git)==null?void 0:g.updatedTime).toLocaleString()})},r=()=>{const a=Ne(),c=Zt(),u=ht();return N(()=>{var d;return u.value.contributors??a.value.contributors??!0?((d=c.value.git)==null?void 0:d.contributors)??null:null})},o=Ne(),s=t(),i=n(),l=r();return(a,c)=>{const u=bt("ClientOnly");return W(),Z("footer",Hp,[te(s)?(W(),Z("div",Fp,[oe(gt,{class:"meta-item-label",item:te(s)},null,8,["item"])])):Oe("",!0),te(i)?(W(),Z("div",Bp,[he("span",jp,Ie(te(o).lastUpdatedText)+": ",1),oe(u,null,{default:Me(()=>[he("span",Up,Ie(te(i)),1)]),_:1})])):Oe("",!0),te(l)&&te(l).length?(W(),Z("div",Wp,[he("span",Kp,Ie(te(o).contributorsText)+": ",1),he("span",qp,[(W(!0),Z(ye,null,zt(te(l),(f,d)=>(W(),Z(ye,{key:d},[he("span",{class:"contributor",title:`email: ${f.email}`},Ie(f.name),9,Gp),d!==te(l).length-1?(W(),Z(ye,{key:0},[$t(", ")],64)):Oe("",!0)],64))),128))])])):Oe("",!0)])}}}),Jp=Ae(Yp,[["__file","PageMeta.vue"]]),Xp=["aria-label"],Qp={class:"inner"},Zp={key:0,class:"prev"},em={key:1,class:"next"},tm=me({__name:"PageNav",setup(e){const t=(u,f)=>f===!1?null:dt(f)?ns(u,f):Wo(f)?f:!1,n=(u,f,d)=>{const m=u.findIndex(g=>g.link===f);if(m!==-1){const g=u[m+d];return g!=null&&g.link?g:null}for(const g of u)if(g.children){const y=n(g.children,f,d);if(y)return y}return null},r=ht(),o=rs(),s=En(),i=Mt(),l=N(()=>{const u=t(i,r.value.prev);return u!==!1?u:n(o.value,s.path,-1)}),a=N(()=>{const u=t(i,r.value.next);return u!==!1?u:n(o.value,s.path,1)}),c=N(()=>Ne().value.pageNavbarLabel??"page navigation");return(u,f)=>l.value||a.value?(W(),Z("nav",{key:0,class:"page-nav","aria-label":c.value},[he("p",Qp,[l.value?(W(),Z("span",Zp,[oe(gt,{item:l.value},null,8,["item"])])):Oe("",!0),a.value?(W(),Z("span",em,[oe(gt,{item:a.value},null,8,["item"])])):Oe("",!0)])],8,Xp)):Oe("",!0)}}),nm=Ae(tm,[["__file","PageNav.vue"]]),rm={class:"page"},om={class:"theme-default-content"},sm=me({__name:"Page",setup(e){return(t,n)=>{const r=bt("Content");return W(),Z("main",rm,[Ee(t.$slots,"top"),he("div",om,[Ee(t.$slots,"content-top"),oe(r),Ee(t.$slots,"content-bottom")]),oe(Jp),oe(nm),Ee(t.$slots,"bottom")])}}}),im=Ae(sm,[["__file","Page.vue"]]),lm={class:"sidebar-item-children"},am=me({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(e){const t=e,{item:n,depth:r}=Mo(t),o=En(),s=Mt(),i=N(()=>Ql(n.value,o)),l=N(()=>({"sidebar-item":!0,"sidebar-heading":r.value===0,active:i.value,collapsible:n.value.collapsible})),a=N(()=>n.value.collapsible?i.value:!0),[c,u]=Td(a.value),f=m=>{n.value.collapsible&&(m.preventDefault(),u())},d=s.afterEach(m=>{bn(()=>{c.value=a.value})});return kr(()=>{d()}),(m,g)=>{var L;const y=bt("SidebarItem",!0);return W(),Z("li",null,[te(n).link?(W(),Re(gt,{key:0,class:Ge(l.value),item:te(n)},null,8,["class","item"])):(W(),Z("p",{key:1,tabindex:"0",class:Ge(l.value),onClick:f,onKeydown:Iu(f,["enter"])},[$t(Ie(te(n).text)+" ",1),te(n).collapsible?(W(),Z("span",{key:0,class:Ge(["arrow",te(c)?"down":"right"])},null,2)):Oe("",!0)],34)),(L=te(n).children)!=null&&L.length?(W(),Re(oa,{key:2},{default:Me(()=>[Er(he("ul",lm,[(W(!0),Z(ye,null,zt(te(n).children,A=>(W(),Re(y,{key:`${te(r)}${A.text}${A.link}`,item:A,depth:te(r)+1},null,8,["item","depth"]))),128))],512),[[Pr,te(c)]])]),_:1})):Oe("",!0)])}}}),cm=Ae(am,[["__file","SidebarItem.vue"]]),um={key:0,class:"sidebar-items"},fm=me({__name:"SidebarItems",setup(e){const t=En(),n=rs();return We(()=>{He(()=>t.hash,r=>{const o=document.querySelector(".sidebar");if(!o)return;const s=document.querySelector(`.sidebar a.sidebar-item[href="${t.path}${r}"]`);if(!s)return;const{top:i,height:l}=o.getBoundingClientRect(),{top:a,height:c}=s.getBoundingClientRect();ai+l&&s.scrollIntoView(!1)})}),(r,o)=>te(n).length?(W(),Z("ul",um,[(W(!0),Z(ye,null,zt(te(n),s=>(W(),Re(cm,{key:`${s.text}${s.link}`,item:s},null,8,["item"]))),128))])):Oe("",!0)}}),dm=Ae(fm,[["__file","SidebarItems.vue"]]),hm={class:"sidebar"},pm=me({__name:"Sidebar",setup(e){return(t,n)=>(W(),Z("aside",hm,[oe(sa),Ee(t.$slots,"top"),oe(dm),Ee(t.$slots,"bottom")]))}}),mm=Ae(pm,[["__file","Sidebar.vue"]]),vm=me({__name:"Layout",setup(e){const t=Zt(),n=ht(),r=Ne(),o=N(()=>n.value.navbar!==!1&&r.value.navbar!==!1),s=rs(),i=ue(!1),l=L=>{i.value=typeof L=="boolean"?L:!i.value},a={x:0,y:0},c=L=>{a.x=L.changedTouches[0].clientX,a.y=L.changedTouches[0].clientY},u=L=>{const A=L.changedTouches[0].clientX-a.x,C=L.changedTouches[0].clientY-a.y;Math.abs(A)>Math.abs(C)&&Math.abs(A)>40&&(A>0&&a.x<=80?l(!0):l(!1))},f=N(()=>[{"no-navbar":!o.value,"no-sidebar":!s.value.length,"sidebar-open":i.value},n.value.pageClass]);let d;We(()=>{d=Mt().afterEach(()=>{l(!1)})}),zr(()=>{d()});const m=Jl(),g=m.resolve,y=m.pending;return(L,A)=>(W(),Z("div",{class:Ge(["theme-container",f.value]),onTouchstart:c,onTouchend:u},[Ee(L.$slots,"navbar",{},()=>[o.value?(W(),Re(Np,{key:0,onToggleSidebar:l},{before:Me(()=>[Ee(L.$slots,"navbar-before")]),after:Me(()=>[Ee(L.$slots,"navbar-after")]),_:3})):Oe("",!0)]),he("div",{class:"sidebar-mask",onClick:A[0]||(A[0]=C=>l(!1))}),Ee(L.$slots,"sidebar",{},()=>[oe(mm,null,{top:Me(()=>[Ee(L.$slots,"sidebar-top")]),bottom:Me(()=>[Ee(L.$slots,"sidebar-bottom")]),_:3})]),Ee(L.$slots,"page",{},()=>[te(n).home?(W(),Re(ip,{key:0})):(W(),Re(Jn,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:te(g),onBeforeLeave:te(y)},{default:Me(()=>[(W(),Re(im,{key:te(t).path},{top:Me(()=>[Ee(L.$slots,"page-top")]),"content-top":Me(()=>[Ee(L.$slots,"page-content-top")]),"content-bottom":Me(()=>[Ee(L.$slots,"page-content-bottom")]),bottom:Me(()=>[Ee(L.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}}),_m=Ae(vm,[["__file","Layout.vue"]]),gm={class:"theme-container"},bm={class:"page"},Em={class:"theme-default-content"},ym=he("h1",null,"404",-1),Lm=me({__name:"NotFound",setup(e){const t=Qn(),n=Ne(),r=n.value.notFound??["Not Found"],o=()=>r[Math.floor(Math.random()*r.length)],s=n.value.home??t.value,i=n.value.backToHome??"Back to home";return(l,a)=>{const c=bt("RouterLink");return W(),Z("div",gm,[he("main",bm,[he("div",Em,[ym,he("blockquote",null,Ie(o()),1),oe(c,{to:te(s)},{default:Me(()=>[$t(Ie(te(i)),1)]),_:1},8,["to"])])])])}}}),wm=Ae(Lm,[["__file","NotFound.vue"]]),Tm=Ft({enhance({app:e,router:t}){oo("Badge")||e.component("Badge",Eh),oo("CodeGroup")||e.component("CodeGroup",yh),oo("CodeGroupItem")||e.component("CodeGroupItem",wh),e.component("AutoLinkExternalIcon",()=>{const r=e.component("ExternalLinkIcon");return r?_e(r):null}),e.component("NavbarSearch",()=>{const r=e.component("Docsearch")||e.component("SearchBox");return r?_e(r):null});const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...r)=>(await Jl().wait(),n(...r))},setup(){Ah(),Sh()},layouts:{Layout:_m,NotFound:wm}}),Am={enhance:({app:e})=>{e.component("NpmBadge",x(()=>v(()=>import("./NpmBadge-1iY0c1kz.js"),__vite__mapDeps([]))))}},dr=[dd,Nd,Wd,Jd,ch,hh,gh,Tm,Am],Pm=[["v-8daa1a0e","/",{title:"Home"},["/README.md"]],["v-b9e358fe","/plugins/active-header-links.html",{title:"active-header-links"},[":md"]],["v-2f74e658","/plugins/back-to-top.html",{title:"back-to-top"},[":md"]],["v-3f3ff580","/plugins/container.html",{title:"container"},[":md"]],["v-3b611b3c","/plugins/copy-code.html",{title:"copy-code"},[":md"]],["v-3aef69fa","/plugins/copyright.html",{title:"copyright"},[":md"]],["v-e97df6fe","/plugins/docsearch.html",{title:"docsearch"},[":md"]],["v-1dcbe4a7","/plugins/external-link-icon.html",{title:"external-link-icon"},[":md"]],["v-561922cf","/plugins/git.html",{title:"git"},[":md"]],["v-190944c2","/plugins/google-analytics.html",{title:"google-analytics"},[":md"]],["v-145379d4","/plugins/medium-zoom.html",{title:"medium-zoom"},[":md"]],["v-481bcda6","/plugins/nprogress.html",{title:"nprogress"},[":md"]],["v-304d59c6","/plugins/palette.html",{title:"palette"},[":md"]],["v-fbc77552","/plugins/prismjs.html",{title:"prismjs"},[":md"]],["v-60cd04a8","/plugins/pwa-popup.html",{title:"pwa-popup"},[":md"]],["v-7da23f32","/plugins/pwa.html",{title:"pwa"},[":md"]],["v-6553daba","/plugins/reading-time.html",{title:"reading-time"},[":md"]],["v-6a3f6e16","/plugins/redirect.html",{title:"redirect"},[":md"]],["v-3203da3e","/plugins/register-components.html",{title:"register-components"},[":md"]],["v-cfa3cd00","/plugins/remove-pwa.html",{title:"remove-pwa"},[":md"]],["v-0503d737","/plugins/rtl.html",{title:"rtl"},[":md"]],["v-12124eae","/plugins/search.html",{title:"search"},[":md"]],["v-35900b8f","/plugins/shiki.html",{title:"shiki"},[":md"]],["v-4091b4e3","/plugins/theme-data.html",{title:"theme-data"},[":md"]],["v-862929ce","/plugins/toc.html",{title:"toc"},[":md"]],["v-2d0ad528","/zh/",{title:"首页"},["/zh/README.md"]],["v-6203866e","/plugins/feed/",{title:"feed"},["/plugins/feed/README.md"]],["v-34d6c4ff","/plugins/feed/channel.html",{title:"Channel Config"},[":md"]],["v-31d2926e","/plugins/feed/config.html",{title:"Plugin Config"},[":md"]],["v-42f5c890","/plugins/feed/frontmatter.html",{title:"Frontmatter Config"},[":md"]],["v-619e6245","/plugins/feed/getter.html",{title:"Feed Getter"},[":md"]],["v-2c2418e6","/plugins/feed/guide.html",{title:"Guide"},[":md"]],["v-873e9c0c","/plugins/seo/",{title:"seo"},["/plugins/seo/README.md"]],["v-1cc5a49d","/plugins/seo/config.html",{title:"Config"},[":md"]],["v-4c7e84d7","/plugins/seo/guide.html",{title:"Guide"},[":md"]],["v-37b048a2","/plugins/sitemap/",{title:"sitemap"},["/plugins/sitemap/README.md"]],["v-24de5616","/plugins/sitemap/config.html",{title:"Config"},[":md"]],["v-4fbe5591","/plugins/sitemap/frontmatter.html",{title:"Frontmatter"},[":md"]],["v-30402d02","/plugins/sitemap/guide.html",{title:"Guide"},[":md"]],["v-e78362cc","/themes/default/",{title:"Usage"},["/themes/default/README.md"]],["v-54b6b4ae","/themes/default/components.html",{title:"Built-in Components"},[":md"]],["v-7ed43806","/themes/default/config.html",{title:"Config"},[":md"]],["v-3fd0046b","/themes/default/extending.html",{title:"Extending"},[":md"]],["v-3e316b89","/themes/default/frontmatter.html",{title:"Frontmatter"},[":md"]],["v-319287e5","/themes/default/locale.html",{title:"Locale Config"},[":md"]],["v-66d0bcf0","/themes/default/markdown.html",{title:"Markdown"},[":md"]],["v-15ee72a8","/themes/default/plugin.html",{title:"Plugins Config"},[":md"]],["v-392d5b46","/themes/default/styles.html",{title:"Styles"},[":md"]],["v-6981b9b0","/zh/plugins/active-header-links.html",{title:"active-header-links"},[":md"]],["v-c1a628f2","/zh/plugins/back-to-top.html",{title:"back-to-top"},[":md"]],["v-5a457a6f","/zh/plugins/container.html",{title:"container"},[":md"]],["v-5666a02b","/zh/plugins/copy-code.html",{title:"copy-code"},[":md"]],["v-55f4eee9","/zh/plugins/copyright.html",{title:"copyright"},[":md"]],["v-b372ed20","/zh/plugins/docsearch.html",{title:"docsearch"},[":md"]],["v-f16906d0","/zh/plugins/external-link-icon.html",{title:"external-link-icon"},[":md"]],["v-1db67204","/zh/plugins/git.html",{title:"git"},[":md"]],["v-6f323dd0","/zh/plugins/google-analytics.html",{title:"google-analytics"},[":md"]],["v-658e4845","/zh/plugins/medium-zoom.html",{title:"medium-zoom"},[":md"]],["v-63215295","/zh/plugins/nprogress.html",{title:"nprogress"},[":md"]],["v-577d4e75","/zh/plugins/palette.html",{title:"palette"},[":md"]],["v-ad678bf4","/zh/plugins/prismjs.html",{title:"prismjs"},[":md"]],["v-7bd28997","/zh/plugins/pwa-popup.html",{title:"pwa-popup"},[":md"]],["v-5c3a8496","/zh/plugins/pwa.html",{title:"pwa"},[":md"]],["v-549eb354","/zh/plugins/reading-time.html",{title:"reading-time"},[":md"]],["v-eca22bb4","/zh/plugins/redirect.html",{title:"redirect"},[":md"]],["v-a51d0de0","/zh/plugins/register-components.html",{title:"register-components"},[":md"]],["v-444d9b1e","/zh/plugins/remove-pwa.html",{title:"remove-pwa"},[":md"]],["v-bfe10934","/zh/plugins/rtl.html",{title:"rtl"},[":md"]],["v-72a3dbcc","/zh/plugins/search.html",{title:"search"},[":md"]],["v-3c435ffe","/zh/plugins/shiki.html",{title:"shiki"},[":md"]],["v-f3866458","/zh/plugins/theme-data.html",{title:"theme-data"},[":md"]],["v-57f70f48","/zh/plugins/toc.html",{title:"toc"},[":md"]],["v-21359efa","/zh/plugins/feed/",{title:"feed"},["/zh/plugins/feed/README.md"]],["v-87c134a0","/zh/plugins/feed/channel.html",{title:"频道设置"},[":md"]],["v-bcead0c6","/zh/plugins/feed/config.html",{title:"插件配置"},[":md"]],["v-5a3bfbe9","/zh/plugins/feed/frontmatter.html",{title:"Frontmatter 配置"},[":md"]],["v-5d533118","/zh/plugins/feed/getter.html",{title:"Feed 获取器"},[":md"]],["v-71cf31d7","/zh/plugins/feed/guide.html",{title:"指南"},[":md"]],["v-60e874ae","/zh/plugins/seo/",{title:"seo"},["/zh/plugins/seo/README.md"]],["v-6270bd8e","/zh/plugins/seo/config.html",{title:"选项"},[":md"]],["v-678409c6","/zh/plugins/seo/guide.html",{title:"指南"},[":md"]],["v-5a88265e","/zh/plugins/sitemap/",{title:"sitemap"},["/zh/plugins/sitemap/README.md"]],["v-7597e534","/zh/plugins/sitemap/config.html",{title:"配置"},[":md"]],["v-1631bbc0","/zh/plugins/sitemap/frontmatter.html",{title:"Frontmatter"},[":md"]],["v-49ab5eee","/zh/plugins/sitemap/guide.html",{title:"指南"},[":md"]],["v-2e25e88b","/zh/themes/default/",{title:"使用方法"},["/zh/themes/default/README.md"]],["v-5e6c68d0","/zh/themes/default/components.html",{title:"内置组件"},[":md"]],["v-2261596c","/zh/themes/default/config.html",{title:"配置"},[":md"]],["v-88f236c8","/zh/themes/default/extending.html",{title:"继承"},[":md"]],["v-b09df90c","/zh/themes/default/frontmatter.html",{title:"Frontmatter"},[":md"]],["v-d9440558","/zh/themes/default/locale.html",{title:"语言配置"},[":md"]],["v-f8d6dac2","/zh/themes/default/markdown.html",{title:"Markdown"},[":md"]],["v-56d43c1b","/zh/themes/default/plugin.html",{title:"插件配置"},[":md"]],["v-4534c7cc","/zh/themes/default/styles.html",{title:"样式"},[":md"]],["v-3706649a","/404.html",{title:""},[]]];var _i=me({name:"Vuepress",setup(){const e=Xu();return()=>_e(e.value)}}),Rm=()=>Pm.reduce((e,[t,n,r,o])=>(e.push({name:t,path:n,component:_i,meta:r},{path:n.endsWith("/")?n+"index.html":n.substring(0,n.length-5),redirect:n},...o.map(s=>({path:s===":md"?n.substring(0,n.length-5)+".md":s,redirect:n}))),e),[{name:"404",path:"/:catchAll(.*)",component:_i}]),Om=Lf,Cm=()=>{const e=sd({history:Om(bl("/ecosystem/")),routes:Rm(),scrollBehavior:(t,n,r)=>r||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{var r;(t.path!==n.path||n===vt)&&([t.meta._data]=await Promise.all([mt.resolvePageData(t.name),(r=Ll[t.name])==null?void 0:r.__asyncLoader()]))}),e},Im=e=>{e.component("ClientOnly",Go),e.component("Content",nf)},xm=(e,t,n)=>{const r=N(()=>t.currentRoute.value.path),o=hd(r,()=>t.currentRoute.value.meta._data),s=N(()=>mt.resolveLayouts(n)),i=N(()=>mt.resolveRouteLocale(an.value.locales,r.value)),l=N(()=>mt.resolveSiteLocaleData(an.value,i.value)),a=N(()=>mt.resolvePageFrontmatter(o.value)),c=N(()=>mt.resolvePageHeadTitle(o.value,l.value)),u=N(()=>mt.resolvePageHead(c.value,a.value,l.value)),f=N(()=>mt.resolvePageLang(o.value,l.value)),d=N(()=>mt.resolvePageLayout(o.value,s.value));return e.provide(Ku,s),e.provide(wl,o),e.provide(Tl,a),e.provide(Yu,c),e.provide(Al,u),e.provide(Pl,f),e.provide(Rl,d),e.provide(Ko,i),e.provide(Cl,l),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>a.value},$head:{get:()=>u.value},$headTitle:{get:()=>c.value},$lang:{get:()=>f.value},$page:{get:()=>o.value},$routeLocale:{get:()=>i.value},$site:{get:()=>an.value},$siteLocale:{get:()=>l.value},$withBase:{get:()=>Yo}}),{layouts:s,pageData:o,pageFrontmatter:a,pageHead:u,pageHeadTitle:c,pageLang:f,pageLayout:d,routeLocale:i,siteData:an,siteLocaleData:l}},Sm=()=>{const e=Gu(),t=Ju();let n=[];const r=()=>{e.value.forEach(i=>{const l=Dm(i);l&&n.push(l)})},o=()=>{const i=[];return e.value.forEach(l=>{const a=km(l);a&&i.push(a)}),i},s=()=>{document.documentElement.lang=t.value;const i=o();n.forEach((l,a)=>{const c=i.findIndex(u=>l.isEqualNode(u));c===-1?(l.remove(),delete n[a]):i.splice(c,1)}),i.forEach(l=>document.head.appendChild(l)),n=[...n.filter(l=>!!l),...i]};Qt(Zu,s),We(()=>{r(),He(e,s,{immediate:!1})})},Dm=([e,t,n=""])=>{const r=Object.entries(t).map(([l,a])=>dt(a)?`[${l}=${JSON.stringify(a)}]`:a===!0?`[${l}]`:"").join(""),o=`head > ${e}${r}`;return Array.from(document.querySelectorAll(o)).find(l=>l.innerText===n)||null},km=([e,t,n])=>{if(!dt(e))return null;const r=document.createElement(e);return Wo(t)&&Object.entries(t).forEach(([o,s])=>{dt(s)?r.setAttribute(o,s):s===!0&&r.setAttribute(o,"")}),dt(n)&&r.appendChild(document.createTextNode(n)),r},zm=Du,Vm=async()=>{var n;const e=zm({name:"VuepressApp",setup(){var r;Sm();for(const o of dr)(r=o.setup)==null||r.call(o);return()=>[_e(Hl),...dr.flatMap(({rootComponents:o=[]})=>o.map(s=>_e(s)))]}}),t=Cm();Im(e),xm(e,t,dr);for(const r of dr)await((n=r.enhance)==null?void 0:n.call(r,{app:e,router:t,siteData:an}));return e.use(t),{app:e,router:t}};Vm().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{Ae as _,oe as a,he as b,Z as c,Vm as createVueApp,$t as d,Gc as e,me as f,N as g,W as o,bt as r,Me as w}; +function __vite__mapDeps(indexes) { + if (!__vite__mapDeps.viteFileDeps) { + __vite__mapDeps.viteFileDeps = ["assets/extending.html-TWRhgpOs.js","assets/extending-a-theme-01-OfxlnZki.js","assets/extending.html-EihhNNxm.js"] + } + return indexes.map((i) => __vite__mapDeps.viteFileDeps[i]) +} diff --git a/assets/back-to-top.html-J5Qq7R-Y.js b/assets/back-to-top.html-J5Qq7R-Y.js new file mode 100644 index 0000000000..58ae9781be --- /dev/null +++ b/assets/back-to-top.html-J5Qq7R-Y.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2f74e658","path":"/plugins/back-to-top.html","title":"back-to-top","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"threshold","slug":"threshold","link":"#threshold","children":[]},{"level":3,"title":"progress","slug":"progress","link":"#progress","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/back-to-top.md"}');export{e as data}; diff --git a/assets/back-to-top.html-Lcuz8vr_.js b/assets/back-to-top.html-Lcuz8vr_.js new file mode 100644 index 0000000000..2f09c5158d --- /dev/null +++ b/assets/back-to-top.html-Lcuz8vr_.js @@ -0,0 +1,15 @@ +import{_ as n,r as e,o as l,c as o,a as p,b as s,e as t}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"back-to-top",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#back-to-top"},[s("span",null,"back-to-top")])],-1),i=t(`

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

This plugin has been integrated into the default theme.

Usage

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

Options

threshold

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

progress

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

Styles

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

:root {
+  --back-to-top-z-index: 5;
+  --back-to-top-icon: url("back-to-top.svg");
+  --back-to-top-bg-color: #fff;
+  --back-to-top-color: #3eaf7c;
+  --back-to-top-color-hover: #71cda3;
+  --back-to-top-shadow: rgb(0 0 0 / 20%);
+}
+
`,13);function d(D,u){const a=e("NpmBadge");return l(),o("div",null,[r,p(a,{package:"@vuepress/plugin-back-to-top"}),i])}const b=n(c,[["render",d],["__file","back-to-top.html.vue"]]);export{b as default}; diff --git a/assets/back-to-top.html-sQ7U_G8N.js b/assets/back-to-top.html-sQ7U_G8N.js new file mode 100644 index 0000000000..ec35a6b5a9 --- /dev/null +++ b/assets/back-to-top.html-sQ7U_G8N.js @@ -0,0 +1,15 @@ +import{_ as n,r as l,o as e,c as o,a as p,b as s,e as c}from"./app-GUhkEPRO.js";const r={},t=s("h1",{id:"back-to-top",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#back-to-top"},[s("span",null,"back-to-top")])],-1),i=c(`

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

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

使用方法

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

选项

threshold

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

progress

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

样式

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

:root {
+  --back-to-top-z-index: 5;
+  --back-to-top-icon: url("back-to-top.svg");
+  --back-to-top-bg-color: #fff;
+  --back-to-top-color: #3eaf7c;
+  --back-to-top-color-hover: #71cda3;
+  --back-to-top-shadow: rgb(0 0 0 / 20%);
+}
+
`,13);function d(D,u){const a=l("NpmBadge");return e(),o("div",null,[t,p(a,{package:"@vuepress/plugin-back-to-top"}),i])}const b=n(r,[["render",d],["__file","back-to-top.html.vue"]]);export{b as default}; diff --git a/assets/back-to-top.html-uUD76HWZ.js b/assets/back-to-top.html-uUD76HWZ.js new file mode 100644 index 0000000000..b1cd7eca84 --- /dev/null +++ b/assets/back-to-top.html-uUD76HWZ.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-c1a628f2","path":"/zh/plugins/back-to-top.html","title":"back-to-top","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"threshold","slug":"threshold","link":"#threshold","children":[]},{"level":3,"title":"progress","slug":"progress","link":"#progress","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/back-to-top.md"}');export{e as data}; diff --git a/assets/channel.html-97Rgn5HR.js b/assets/channel.html-97Rgn5HR.js new file mode 100644 index 0000000000..6bbbc2f2f1 --- /dev/null +++ b/assets/channel.html-97Rgn5HR.js @@ -0,0 +1,15 @@ +import{_ as s,r as l,o as i,c,b as n,d as a,a as o,e as t}from"./app-GUhkEPRO.js";const r={},p=t(`

频道设置

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

channel.title

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

频道的标题

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

频道地址

channel.description

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

频道描述信息

channel.language

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

频道使用的语言

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

频道版权信息

channel.pubDate

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

频道内容的发布时间

channel.lastUpdated

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

频道内容的上次更新时间

channel.ttl

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

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

channel.image

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

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

channel.icon

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

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

channel.author

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

频道的作者。

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

channel.hub

  • 类型:string

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

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

Channel Config

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

channel.title

  • Type: string
  • Default: SiteConfig.title

Channel title

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

Channel address

channel.description

  • Type: string
  • Default: SiteConfig.description

Channel description

channel.language

  • Type: string

  • Default:

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

The language of the channel

  • Type: string

  • Default:

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

Channel copyright information

channel.pubDate

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

Publish date of the Channel

channel.lastUpdated

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

Last update time of channel content

channel.ttl

  • Type: number
  • Recommended to set manually: Yes

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

channel.image

  • Type: string
  • Recommended to set manually: Yes

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

channel.icon

  • Type: string
  • Recommended to set manually: Yes

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

channel.author

  • Type: FeedAuthor
  • Recommended to set manually: Yes

The author of the channel.

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

channel.hub

  • Type: string

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

`,5),u={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"WebSub",-1),g={href:"https://w3c.github.io/websub/#subscription-migration",target:"_blank",rel:"noopener noreferrer"};function b(y,f){const s=i("ExternalLinkIcon");return t(),c("div",null,[p,e("details",d,[h,e("div",u,[m,e("p",null,[n("For details, see "),e("a",g,[n("Websub"),o(s)]),n(".")])])])])}const D=l(r,[["render",b],["__file","channel.html.vue"]]);export{D as default}; diff --git a/assets/channel.html-uSvMSuIK.js b/assets/channel.html-uSvMSuIK.js new file mode 100644 index 0000000000..435297e3f5 --- /dev/null +++ b/assets/channel.html-uSvMSuIK.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-34d6c4ff","path":"/plugins/feed/channel.html","title":"Channel Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"channel.title","slug":"channel-title","link":"#channel-title","children":[]},{"level":2,"title":"channel.link","slug":"channel-link","link":"#channel-link","children":[]},{"level":2,"title":"channel.description","slug":"channel-description","link":"#channel-description","children":[]},{"level":2,"title":"channel.language","slug":"channel-language","link":"#channel-language","children":[]},{"level":2,"title":"channel.copyright","slug":"channel-copyright","link":"#channel-copyright","children":[]},{"level":2,"title":"channel.pubDate","slug":"channel-pubdate","link":"#channel-pubdate","children":[]},{"level":2,"title":"channel.lastUpdated","slug":"channel-lastupdated","link":"#channel-lastupdated","children":[]},{"level":2,"title":"channel.ttl","slug":"channel-ttl","link":"#channel-ttl","children":[]},{"level":2,"title":"channel.image","slug":"channel-image","link":"#channel-image","children":[]},{"level":2,"title":"channel.icon","slug":"channel-icon","link":"#channel-icon","children":[]},{"level":2,"title":"channel.author","slug":"channel-author","link":"#channel-author","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/channel.md"}');export{l as data}; diff --git a/assets/components.html-5eAAqCkE.js b/assets/components.html-5eAAqCkE.js new file mode 100644 index 0000000000..8055b03289 --- /dev/null +++ b/assets/components.html-5eAAqCkE.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-54b6b4ae","path":"/themes/default/components.html","title":"Built-in Components","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Badge","slug":"badge","link":"#badge","children":[]},{"level":2,"title":"CodeGroup","slug":"codegroup","link":"#codegroup","children":[]},{"level":2,"title":"CodeGroupItem","slug":"codegroupitem","link":"#codegroupitem","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/components.md"}');export{e as data}; diff --git a/assets/components.html-CQjk7rXv.js b/assets/components.html-CQjk7rXv.js new file mode 100644 index 0000000000..8eccceea92 --- /dev/null +++ b/assets/components.html-CQjk7rXv.js @@ -0,0 +1,32 @@ +import{_ as u,r as l,o as m,c as v,a as n,b as e,d as s,w as a,e as i}from"./app-GUhkEPRO.js";const D={},b=e("h1",{id:"built-in-components",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#built-in-components"},[e("span",null,"Built-in Components")])],-1),h={id:"badge",tabindex:"-1"},g={class:"header-anchor",href:"#badge"},y=i(`
  • Props:

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

Input

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

Output

`,4),_=i(`

CodeGroup

CodeGroupItem

  • Props:

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

    This component must be placed inside a CodeGroup component.

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

  • Example:

Input

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

Output

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

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

输入

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

输出

`,4),y=i(`

CodeGroup

CodeGroupItem

  • Props:

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

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

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

  • 示例:

输入

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

输出

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

Config

hostname

  • Type: string

  • Required: Yes

  • Details:

    Deploy hostname.

author

  • Type: Author

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

  • Details:

    Default author.

autoDescription

  • Type: boolean

  • Default: true

  • Details:

    Whether generate description automatically

canonical

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

  • Details:

    Canonical link

fallBackImage

  • Type: string

  • Details:

    Fallback Image link when no image are found

restrictions

  • Type: string

  • Details:

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

twitterID

  • Type: string

  • Details:

    Fill in your twitter username.

isArticle

  • Type: (page: Page) => boolean

  • Details:

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

ogp

  • Type:

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

  • Details:

    Custom OPG Generator.

    You can use this options to edit OGP tags.

jsonLd

  • Type:

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

  • Details:

    Custom JSON-LD Generator.

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

customHead

  • Type:

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

  • Details:

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

`,23),i=[p];function o(c,t){return a(),n("div",null,i)}const d=s(e,[["render",o],["__file","config.html.vue"]]);export{d as default}; diff --git a/assets/config.html-9JbPInT0.js b/assets/config.html-9JbPInT0.js new file mode 100644 index 0000000000..ee93eb6cd2 --- /dev/null +++ b/assets/config.html-9JbPInT0.js @@ -0,0 +1,18 @@ +import{_ as t,r as p,o as i,c as r,b as e,d as s,a as l,w as n,e as o}from"./app-GUhkEPRO.js";const c={},d=o(`

Plugin Config

hostname

  • Type: string
  • Required: Yes

The domain name of the deployment site.

atom

  • Type: boolean
  • Default: false

Whether to output Atom syntax files.

json

  • Type: boolean
  • Default: false

Whether output JSON syntax files.

rss

  • Type: boolean
  • Default: false

Whether to output RSS syntax files.

image

  • Type: string

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

icon

  • Type: string

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

count

  • Type: number
  • Default: 100

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

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

preservedElements

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

Custom element or component which should be preserved in feed.

By default, all unknown tags will be removed.

filter

  • Type: (page: Page)=> boolean

  • Default:

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

A custom filter function, used to filter feed items.

sorter

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

  • Default:

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

Custom sorter function for feed items.

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

TIP

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

channel

channel option is used to config Feed Channels.

`,37),D=o('

devServer

  • Type: boolean
  • Default: false

Whether enabled in devServer.

TIP

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

devHostname

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

Hostname to use in devServer

atomOutputFilename

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

Atom syntax output filename, relative to dest folder.

atomXslTemplate

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

Atom xsl template file content.

atomXslFilename

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

Atom xsl filename, relative to dest folder.

jsonOutputFilename

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

JSON syntax output filename, relative to dest folder.

rssOutputFilename

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

RSS syntax output filename, relative to dest folder.

rssXslTemplate

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

RSS xsl template file content.

rssXslFilename

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

RSS syntax xsl filename, relative to dest folder.

getter

',29),u=e("div",{class:"custom-container tip"},[e("p",{class:"custom-container-title"},"The plugin has a built-in getter, only set this if you want full control of feed generation.")],-1),h=e("h2",{id:"locales",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locales"},[e("span",null,"locales")])],-1),m=e("ul",null,[e("li",null,[s("Type: "),e("code",null,"Record")])],-1),f=e("p",null,"You can use it to specific options for each locale.",-1),y=e("p",null,[s("Any options above are supported except "),e("code",null,"hostname"),s(".")],-1);function C(g,v){const a=p("RouterLink");return i(),r("div",null,[d,e("p",null,[s("For available options, please see "),l(a,{to:"/plugins/feed/channel.html"},{default:n(()=>[s("Config → Channel")]),_:1})]),D,e("p",null,[s("Feed generation controller, see "),l(a,{to:"/plugins/feed/getter.html"},{default:n(()=>[s("Feed Getter")]),_:1}),s(".")]),u,h,m,f,y])}const x=t(c,[["render",C],["__file","config.html.vue"]]);export{x as default}; diff --git a/assets/config.html-BVRB3Svi.js b/assets/config.html-BVRB3Svi.js new file mode 100644 index 0000000000..174c143933 --- /dev/null +++ b/assets/config.html-BVRB3Svi.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-1cc5a49d","path":"/plugins/seo/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"author","slug":"author","link":"#author","children":[]},{"level":2,"title":"autoDescription","slug":"autodescription","link":"#autodescription","children":[]},{"level":2,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":2,"title":"fallBackImage","slug":"fallbackimage","link":"#fallbackimage","children":[]},{"level":2,"title":"restrictions","slug":"restrictions","link":"#restrictions","children":[]},{"level":2,"title":"twitterID","slug":"twitterid","link":"#twitterid","children":[]},{"level":2,"title":"isArticle","slug":"isarticle","link":"#isarticle","children":[]},{"level":2,"title":"ogp","slug":"ogp","link":"#ogp","children":[]},{"level":2,"title":"jsonLd","slug":"jsonld","link":"#jsonld","children":[]},{"level":2,"title":"customHead","slug":"customhead","link":"#customhead","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/seo/config.md"}');export{l as data}; diff --git a/assets/config.html-LeVTWPLA.js b/assets/config.html-LeVTWPLA.js new file mode 100644 index 0000000000..f46865c24d --- /dev/null +++ b/assets/config.html-LeVTWPLA.js @@ -0,0 +1 @@ +import{_ as i,r as s,o as t,c as o,b as e,d as a,a as n,w as c,e as l}from"./app-GUhkEPRO.js";const d={},r=l('

配置

hostname

  • 类型:string

  • 必填:是

  • 详情:

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

extraUrls

  • 类型:string[]

  • 详情:

    需要额外包含的网址。

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

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

excludePaths

  • 类型:string[]

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

  • 详情:

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

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

devServer

  • 类型:boolean

  • 默认值:false

  • 详情:

    是否在开发服务器中启用

提示

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

devHostname

  • 类型:string

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

  • 详情:

    开发服务器使用的主机名

sitemapFilename

  • 类型:string

  • 默认值:"sitemap.xml"

  • 详情:

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

sitemapXSLFilename

  • 类型:string

  • 默认值:"sitemap.xsl"

  • 详情:

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

sitemapXSLTemplate

  • 类型:string

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

  • 详情:

    用作模板的 XSL 文件内容

changefreq

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

priority

  • 类型:number

  • 默认值:0.5

  • 详情:

    页面优先级,范围 01

modifyTimeGetter

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

  • 详情:

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

',4);function x(_,g){const p=s("RouterLink");return t(),o("div",null,[r,e("ul",null,[h,u,e("li",null,[m,e("p",null,[a("页面默认更新频率,会被 Frontmatter 中的 "),n(p,{to:"/zh/plugins/sitemap/frontmatter.html#sitemap-changefreq"},{default:c(()=>[a("changefreq")]),_:1}),a(" 选项覆盖。")])])]),f])}const v=i(d,[["render",x],["__file","config.html.vue"]]);export{v as default}; diff --git a/assets/config.html-PhwKH_Cw.js b/assets/config.html-PhwKH_Cw.js new file mode 100644 index 0000000000..95d34a1c61 --- /dev/null +++ b/assets/config.html-PhwKH_Cw.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-2261596c","path":"/zh/themes/default/config.html","title":"配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"基础配置","slug":"基础配置","link":"#基础配置","children":[{"level":3,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Locale 配置","slug":"locale-配置","link":"#locale-配置","children":[{"level":3,"title":"colorMode","slug":"colormode","link":"#colormode","children":[]},{"level":3,"title":"colorModeSwitch","slug":"colormodeswitch","link":"#colormodeswitch","children":[]},{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"logo","slug":"logo","link":"#logo","children":[]},{"level":3,"title":"logoDark","slug":"logodark","link":"#logodark","children":[]},{"level":3,"title":"logoAlt","slug":"logoalt","link":"#logoalt","children":[]},{"level":3,"title":"repo","slug":"repo","link":"#repo","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"docsRepo","slug":"docsrepo","link":"#docsrepo","children":[]},{"level":3,"title":"docsBranch","slug":"docsbranch","link":"#docsbranch","children":[]},{"level":3,"title":"docsDir","slug":"docsdir","link":"#docsdir","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]}],"git":{"updatedTime":1706734148000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":7}]},"filePathRelative":"zh/themes/default/config.md"}');export{l as data}; diff --git a/assets/config.html-Srk8n1d6.js b/assets/config.html-Srk8n1d6.js new file mode 100644 index 0000000000..2bd948d2cf --- /dev/null +++ b/assets/config.html-Srk8n1d6.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7597e534","path":"/zh/plugins/sitemap/config.html","title":"配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"extraUrls","slug":"extraurls","link":"#extraurls","children":[]},{"level":2,"title":"excludePaths","slug":"excludepaths","link":"#excludepaths","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"sitemapFilename","slug":"sitemapfilename","link":"#sitemapfilename","children":[]},{"level":2,"title":"sitemapXSLFilename","slug":"sitemapxslfilename","link":"#sitemapxslfilename","children":[]},{"level":2,"title":"sitemapXSLTemplate","slug":"sitemapxsltemplate","link":"#sitemapxsltemplate","children":[]},{"level":2,"title":"changefreq","slug":"changefreq","link":"#changefreq","children":[]},{"level":2,"title":"priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"modifyTimeGetter","slug":"modifytimegetter","link":"#modifytimegetter","children":[]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"zh/plugins/sitemap/config.md"}');export{e as data}; diff --git a/assets/config.html-Tqr51Z6v.js b/assets/config.html-Tqr51Z6v.js new file mode 100644 index 0000000000..54cb225592 --- /dev/null +++ b/assets/config.html-Tqr51Z6v.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-6270bd8e","path":"/zh/plugins/seo/config.html","title":"选项","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"author","slug":"author","link":"#author","children":[]},{"level":2,"title":"autoDescription","slug":"autodescription","link":"#autodescription","children":[]},{"level":2,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":2,"title":"fallBackImage","slug":"fallbackimage","link":"#fallbackimage","children":[]},{"level":2,"title":"restrictions","slug":"restrictions","link":"#restrictions","children":[]},{"level":2,"title":"twitterID","slug":"twitterid","link":"#twitterid","children":[]},{"level":2,"title":"isArticle","slug":"isarticle","link":"#isarticle","children":[]},{"level":2,"title":"ogp","slug":"ogp","link":"#ogp","children":[]},{"level":2,"title":"jsonLd","slug":"jsonld","link":"#jsonld","children":[]},{"level":2,"title":"customHead","slug":"customhead","link":"#customhead","children":[]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/seo/config.md"}');export{l as data}; diff --git a/assets/config.html-UsevBMSw.js b/assets/config.html-UsevBMSw.js new file mode 100644 index 0000000000..39b38c166e --- /dev/null +++ b/assets/config.html-UsevBMSw.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-bcead0c6","path":"/zh/plugins/feed/config.html","title":"插件配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"atom","slug":"atom","link":"#atom","children":[]},{"level":2,"title":"json","slug":"json","link":"#json","children":[]},{"level":2,"title":"rss","slug":"rss","link":"#rss","children":[]},{"level":2,"title":"image","slug":"image","link":"#image","children":[]},{"level":2,"title":"icon","slug":"icon","link":"#icon","children":[]},{"level":2,"title":"count","slug":"count","link":"#count","children":[]},{"level":2,"title":"preservedElements","slug":"preservedelements","link":"#preservedelements","children":[]},{"level":2,"title":"filter","slug":"filter","link":"#filter","children":[]},{"level":2,"title":"sorter","slug":"sorter","link":"#sorter","children":[]},{"level":2,"title":"channel","slug":"channel","link":"#channel","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"atomOutputFilename","slug":"atomoutputfilename","link":"#atomoutputfilename","children":[]},{"level":2,"title":"atomXslTemplate","slug":"atomxsltemplate","link":"#atomxsltemplate","children":[]},{"level":2,"title":"atomXslFilename","slug":"atomxslfilename","link":"#atomxslfilename","children":[]},{"level":2,"title":"jsonOutputFilename","slug":"jsonoutputfilename","link":"#jsonoutputfilename","children":[]},{"level":2,"title":"rssOutputFilename","slug":"rssoutputfilename","link":"#rssoutputfilename","children":[]},{"level":2,"title":"rssXslTemplate","slug":"rssxsltemplate","link":"#rssxsltemplate","children":[]},{"level":2,"title":"rssXslFilename","slug":"rssxslfilename","link":"#rssxslfilename","children":[]},{"level":2,"title":"getter","slug":"getter","link":"#getter","children":[]},{"level":2,"title":"locales","slug":"locales","link":"#locales","children":[]}],"git":{"updatedTime":1706854470000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/feed/config.md"}');export{e as data}; diff --git a/assets/config.html-ZkgZP8Et.js b/assets/config.html-ZkgZP8Et.js new file mode 100644 index 0000000000..6e2b227096 --- /dev/null +++ b/assets/config.html-ZkgZP8Et.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-31d2926e","path":"/plugins/feed/config.html","title":"Plugin Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"atom","slug":"atom","link":"#atom","children":[]},{"level":2,"title":"json","slug":"json","link":"#json","children":[]},{"level":2,"title":"rss","slug":"rss","link":"#rss","children":[]},{"level":2,"title":"image","slug":"image","link":"#image","children":[]},{"level":2,"title":"icon","slug":"icon","link":"#icon","children":[]},{"level":2,"title":"count","slug":"count","link":"#count","children":[]},{"level":2,"title":"preservedElements","slug":"preservedelements","link":"#preservedelements","children":[]},{"level":2,"title":"filter","slug":"filter","link":"#filter","children":[]},{"level":2,"title":"sorter","slug":"sorter","link":"#sorter","children":[]},{"level":2,"title":"channel","slug":"channel","link":"#channel","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"atomOutputFilename","slug":"atomoutputfilename","link":"#atomoutputfilename","children":[]},{"level":2,"title":"atomXslTemplate","slug":"atomxsltemplate","link":"#atomxsltemplate","children":[]},{"level":2,"title":"atomXslFilename","slug":"atomxslfilename","link":"#atomxslfilename","children":[]},{"level":2,"title":"jsonOutputFilename","slug":"jsonoutputfilename","link":"#jsonoutputfilename","children":[]},{"level":2,"title":"rssOutputFilename","slug":"rssoutputfilename","link":"#rssoutputfilename","children":[]},{"level":2,"title":"rssXslTemplate","slug":"rssxsltemplate","link":"#rssxsltemplate","children":[]},{"level":2,"title":"rssXslFilename","slug":"rssxslfilename","link":"#rssxslfilename","children":[]},{"level":2,"title":"getter","slug":"getter","link":"#getter","children":[]},{"level":2,"title":"locales","slug":"locales","link":"#locales","children":[]}],"git":{"updatedTime":1706854470000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/feed/config.md"}');export{e as data}; diff --git a/assets/config.html-_tauS4zw.js b/assets/config.html-_tauS4zw.js new file mode 100644 index 0000000000..a4f65813ba --- /dev/null +++ b/assets/config.html-_tauS4zw.js @@ -0,0 +1 @@ +import{_ as i,r as p,o,c as s,b as e,d as a,a as n,w as r,e as l}from"./app-GUhkEPRO.js";const d={},c=l('

Config

hostname

  • Type: string

  • Required: Yes

  • Details:

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

extraUrls

  • Type: string[]

  • Details:

    Extra link to be included.

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

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

excludePaths

  • Type: string[]

  • Default: ['/404.html']

  • Details:

    Urls excluding from sitemap, starting with absolute path.

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

devServer

  • Type: boolean

  • Default: false

  • Details:

    Whether enabled in devServer.

TIP

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

devHostname

  • Type: string

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

  • Details:

    Hostname to use in devServer

sitemapFilename

  • Type: string

  • Default value: "sitemap.xml"

  • Details:

    The output filename, relative to output directory.

sitemapXSLFilename

  • Type: string

  • Default value: "sitemap.xsl"

  • Details:

    Output xsl filename, relative to dest folder.

sitemapXSLTemplate

  • Type: string

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

  • Details:

    XSL content used as template.

changefreq

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

priority

  • Type: number

  • Default: 0.5

  • Details:

    Page priority, from 0 to 1.

modifyTimeGetter

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

  • Details:

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

',4);function y(g,x){const t=p("RouterLink");return o(),s("div",null,[c,e("ul",null,[h,u,e("li",null,[m,e("p",null,[a("Page default update frequency, will be overridden by "),n(t,{to:"/plugins/sitemap/frontmatter.html#sitemap-changefreq"},{default:r(()=>[a("sitemap.changefreq")]),_:1}),a(" in Frontmatter.")])])]),f])}const b=i(d,[["render",y],["__file","config.html.vue"]]);export{b as default}; diff --git a/assets/config.html-bGa-nBlX.js b/assets/config.html-bGa-nBlX.js new file mode 100644 index 0000000000..45df06893f --- /dev/null +++ b/assets/config.html-bGa-nBlX.js @@ -0,0 +1,141 @@ +import{_ as t,r as p,o as c,c as r,b as s,d as e,a as l,w as o,e as n}from"./app-GUhkEPRO.js";const d={},u=n('

Config

Basic Config

hostname

  • Type: string

  • Details:

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

locales

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

Locale Config

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

colorMode

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

  • Default: true

  • Details:

    Enable color mode switching or not.

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

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

    home

    • Type: string

    • Default: /

    • Details:

      Specify the path of the homepage.

      This will be used for:

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

    • Default: []

    • Details:

      Configuration of navbar.

      Set to false to disable navbar.

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

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

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

    • Details:

      Specify the url of logo image.

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

      Set to null to disable logo.

    • Example:

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

    logoDark

    logoAlt

    • Type: null | string

    • Details:

      Specify the alt text of the logo image.

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

    repo

    • Type: string

    • Details:

      Specify the repository url of your project.

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

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

    Set to false to disable sidebar.

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

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

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

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

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

    sidebarDepth

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

    Details:

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

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

    editLinkPattern

    • Type: string

    • Details:

      Specify the pattern of the edit this page link.

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

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

    • Usage:

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

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

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

    docsRepo

    • Type: string

    • Details:

      Specify the repository url of your documentation source files.

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

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

    docsBranch

    • Type: string

    • Default: 'main'

    • Details:

      Specify the repository branch of your documentation source files.

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

    docsDir

    • Type: string

    • Default: ''

    • Details:

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

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

    lastUpdated

    `,11),ss=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),es=s("li",null,[s("p",null,[e("Default: "),s("code",null,"true")])],-1),ls=s("p",null,"Details:",-1),ns=s("p",null,[e("Enable the "),s("em",null,"last updated timestamp"),e(" or not.")],-1),as=s("code",null,"false",-1),os=s("h3",{id:"contributors",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#contributors"},[s("span",null,"contributors")])],-1),is=s("li",null,[s("p",null,[e("Type: "),s("code",null,"boolean")])],-1),ps=s("li",null,[s("p",null,[e("Default: "),s("code",null,"true")])],-1),ts=s("p",null,"Details:",-1),cs=s("p",null,[e("Enable the "),s("em",null,"contributors list"),e(" or not.")],-1),rs=s("code",null,"false",-1);function ds(us,Ds){const a=p("RouterLink"),i=p("ExternalLinkIcon");return c(),r("div",null,[u,s("ul",null,[D,h,s("li",null,[v,b,y,s("p",null,[e("This option will only take effect in default theme, so don't confuse with "),m,e(" in "),l(a,{to:"/themes/default/config.html#locales"},{default:o(()=>[e("Site Config")]),_:1}),e(".")])]),s("li",null,[f,s("ul",null,[s("li",null,[s("a",C,[e("Guide > I18n"),l(i)])])])])]),g,s("ul",null,[E,_,s("li",null,[x,k,s("p",null,[e("If set to "),w,e(", the initial color mode will be automatically set according to "),s("a",F,[e("prefers-color-scheme"),l(i)]),e(".")])]),A]),T,s("ul",null,[S,s("li",null,[I,s("ul",null,[j,s("li",null,[l(a,{to:"/themes/default/locale.html#togglecolormode"},{default:o(()=>[e("Default Theme > Locale Config > toggleColorMode")]),_:1})])])])]),G,s("ul",null,[s("li",null,[e("Also see: "),s("ul",null,[s("li",null,[s("a",N,[e("Guide > Assets > Public Files"),l(i)])])])])]),L,s("ul",null,[B,M,s("li",null,[R,Y,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#sidebar"},{default:o(()=>[e("sidebar")]),_:1}),e(" frontmatter in your pages.")]),P]),U]),V,s("ul",null,[H,z,s("li",null,[O,s("p",null,[e("The max value depends on which levels of headers you have extracted via "),s("a",W,[e("markdown.headers.level"),l(i)]),e(".")]),q,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#sidebardepth"},{default:o(()=>[e("sidebarDepth")]),_:1}),e(" frontmatter in your pages.")])])]),J,s("ul",null,[K,Q,s("li",null,[X,Z,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#editlink"},{default:o(()=>[e("editLink")]),_:1}),e(" frontmatter in your pages.")])])]),$,s("ul",null,[ss,es,s("li",null,[ls,ns,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#lastupdated"},{default:o(()=>[e("lastUpdated")]),_:1}),e(" frontmatter in your pages. Notice that if you have already set this option to "),as,e(", this feature will be disabled totally and could not be enabled in locales nor page frontmatter.")])])]),os,s("ul",null,[is,ps,s("li",null,[ts,cs,s("p",null,[e("You can override this global option via "),l(a,{to:"/themes/default/frontmatter.html#contributors"},{default:o(()=>[e("contributors")]),_:1}),e(" frontmatter in your pages. Notice that if you have already set this option to "),rs,e(", this feature will be disabled totally and could not be enabled in locales nor page frontmatter.")])])])])}const vs=t(d,[["render",ds],["__file","config.html.vue"]]);export{vs as default}; diff --git a/assets/config.html-bycs_hrt.js b/assets/config.html-bycs_hrt.js new file mode 100644 index 0000000000..0c330fa975 --- /dev/null +++ b/assets/config.html-bycs_hrt.js @@ -0,0 +1,45 @@ +import{_ as s,o as n,c as a,e as l}from"./app-GUhkEPRO.js";const e={},p=l(`

    选项

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      部署域名

    author

    • 类型:Author

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

      默认作者

    autoDescription

    • 类型:boolean

    • 默认值:true

    • 详情:

      是否自动生成描述

    canonical

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

    • 详情:

      首选链接

    fallBackImage

    • 类型:string

    • 详情:

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

    restrictions

    • 类型:string

    • 详情:

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

    twitterID

    • 类型:string

    • 详情:

      你的 twitter 用户名

    isArticle

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

    • 详情:

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

    ogp

    • 类型:

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

      自定义 OGP 生成器

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

    jsonLd

    • 类型:

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

      自定义 JSON-LD 生成器

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

    customHead

    • 类型:

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

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

    `,23),i=[p];function o(c,r){return n(),a("div",null,i)}const d=s(e,[["render",o],["__file","config.html.vue"]]);export{d as default}; diff --git a/assets/config.html-eFCZds2u.js b/assets/config.html-eFCZds2u.js new file mode 100644 index 0000000000..1b70db3119 --- /dev/null +++ b/assets/config.html-eFCZds2u.js @@ -0,0 +1,18 @@ +import{_ as p,r as t,o as c,c as i,b as s,d as e,a as l,w as n,e as o}from"./app-GUhkEPRO.js";const r={},d=o(`

    插件配置

    hostname

    • 类型:string
    • 必填:是

    部署网站的域名。

    atom

    • 类型:boolean
    • 默认值:false

    是否启用 Atom 格式输出。

    json

    • 类型:boolean
    • 默认值:false

    是否启用 JSON 格式输出。

    rss

    • 类型:boolean
    • 默认值:false

    是否启用 RSS 格式输出。

    image

    • 类型:string

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

    icon

    • 类型:string

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

    count

    • 类型:number
    • 默认值:100

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

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

    preservedElements

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

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

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

    filter

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

    • 默认值:

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

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

    sorter

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

    • 默认值:

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

    Feed 项目的排序器。

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

    提示

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

    channel

    channel 选项用于配置 Feed 频道。

    `,37),D=o('

    devServer

    • 类型:boolean
    • 默认值:false

    是否在开发服务器中启用

    提示

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

    devHostname

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

    开发服务器使用的主机名

    atomOutputFilename

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

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

    atomXslTemplate

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

    Atom xsl 模板文件没人陪美国

    atomXslFilename

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

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

    jsonOutputFilename

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

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

    rssOutputFilename

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

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

    rssXslTemplate

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

    RSS xsl 模板文件内容。

    rssXslFilename

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

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

    getter

    ',29),h=s("div",{class:"custom-container tip"},[s("p",{class:"custom-container-title"},"此插件内置了生成器,只有当你想完全控制 feed 生成时才需要设置此选项。")],-1),u=s("h2",{id:"locales",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#locales"},[s("span",null,"locales")])],-1),m=s("ul",null,[s("li",null,[e("类型:"),s("code",null,"Record")])],-1),y=s("p",null,"你可以将它用于每个语言环境的特定选项。",-1),C=s("p",null,[e("除 "),s("code",null,"hostname"),e(" 外,上述任何选项均受支持。")],-1);function f(v,g){const a=t("RouterLink");return c(),i("div",null,[d,s("p",null,[e("可用选项详见 "),l(a,{to:"/zh/plugins/feed/channel.html"},{default:n(()=>[e("配置 → 频道设置")]),_:1})]),D,s("p",null,[e("Feed 生成控制器,详见 "),l(a,{to:"/zh/plugins/feed/getter.html"},{default:n(()=>[e("Feed 生成器")]),_:1}),e("。")]),h,u,m,y,C])}const x=p(r,[["render",f],["__file","config.html.vue"]]);export{x as default}; diff --git a/assets/config.html-fdGyVXQz.js b/assets/config.html-fdGyVXQz.js new file mode 100644 index 0000000000..b45aa97c1c --- /dev/null +++ b/assets/config.html-fdGyVXQz.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-24de5616","path":"/plugins/sitemap/config.html","title":"Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"hostname","slug":"hostname","link":"#hostname","children":[]},{"level":2,"title":"extraUrls","slug":"extraurls","link":"#extraurls","children":[]},{"level":2,"title":"excludePaths","slug":"excludepaths","link":"#excludepaths","children":[]},{"level":2,"title":"devServer","slug":"devserver","link":"#devserver","children":[]},{"level":2,"title":"devHostname","slug":"devhostname","link":"#devhostname","children":[]},{"level":2,"title":"sitemapFilename","slug":"sitemapfilename","link":"#sitemapfilename","children":[]},{"level":2,"title":"sitemapXSLFilename","slug":"sitemapxslfilename","link":"#sitemapxslfilename","children":[]},{"level":2,"title":"sitemapXSLTemplate","slug":"sitemapxsltemplate","link":"#sitemapxsltemplate","children":[]},{"level":2,"title":"changefreq","slug":"changefreq","link":"#changefreq","children":[]},{"level":2,"title":"priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"modifyTimeGetter","slug":"modifytimegetter","link":"#modifytimegetter","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/sitemap/config.md"}');export{e as data}; diff --git a/assets/config.html-k_L4yq_7.js b/assets/config.html-k_L4yq_7.js new file mode 100644 index 0000000000..5a64a67ad8 --- /dev/null +++ b/assets/config.html-k_L4yq_7.js @@ -0,0 +1,141 @@ +import{_ as c,r as i,o as r,c as t,b as s,d as n,a as l,w as o,e as a}from"./app-GUhkEPRO.js";const d={},D=a('

    配置

    基础配置

    hostname

    • 类型: string

    • 详情:

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

    locales

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

    Locale 配置

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

    colorMode

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

  • 默认值: true

  • 详情:

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

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

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

    home

    • 类型: string

    • 默认值: /

    • 详情:

      首页的路径。

      它将被用于:

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

    • 默认值: []

    • 详情:

      导航栏配置。

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

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

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

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

    • 详情:

      Logo 图片的 URL。

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

      设置为 null 可以禁用 Logo 。

    • 示例:

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

    logoDark

    • 类型: null | string

    • 详情:

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

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

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

    • 参考:

    logoAlt

    • 类型:null | string

    • 详情:

      指定 Logo 图片的替代文字。

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

    repo

    • 类型: string

    • 详情:

      项目仓库的 URL。

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

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

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

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

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

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

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

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

    sidebarDepth

    `,6),H=s("li",null,[s("p",null,[n("类型: "),s("code",null,"number")])],-1),K=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"2")])],-1),O=a("

    详情:

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

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

    editLinkPattern

    • 类型: string

    • 详情:

      编辑此页 链接的 Pattern 。

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

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

    • 用法:

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

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

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

    docsRepo

    • 类型: string

    • 详情:

      文档源文件的仓库 URL 。

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

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

    docsBranch

    • 类型: string

    • 默认值: 'main'

    • 详情:

      文档源文件的仓库分支。

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

    docsDir

    • 类型: string

    • 默认值: ''

    • 详情:

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

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

    lastUpdated

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

    使用方法

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

    容器语法

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

    提示

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

    配置项

    type

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

    locales

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

    • 详情:

      容器在不同 locales 下的默认 info

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

    • 示例:

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

    before

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

    • 默认值:

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

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

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

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

    after

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

    • 默认值:

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

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

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

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

    render

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

    Usage

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

    Container Syntax

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

    TIP

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

    Options

    type

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

    locales

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

    • Details:

      The default info of the container in different locales.

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

    • Example:

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

    before

    • Type: (info: string) => string

    • Default:

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

      A function to render the starting tag of the container.

      The first param is the info part of container syntax.

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

    after

    • Type: (info: string) => string

    • Default:

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

      A function to render the ending tag of the container.

      The first param is the info part of container syntax.

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

    render

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

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

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

    Usage

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

    Options

    selector

    • Type: string | string[]

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

    • Details:

      Code block selector

    showInMobile

    • Type: boolean

    • Default: false

    • Details:

      Whether to display copy button on the mobile device

    duration

    • Type: number

    • Default: 2000

    • Details:

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

    delay

    • Type: number

    • Default: 800

    • Details:

      The delay of registering copy code buttons, in ms.

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

    locales

    • Type: CopyCodeLocaleConfig

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

    • Details:

      Locales config for copy code plugin.

    • Example:

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

    Styles

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

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: var(--code-ln-color, #9e9e9e);
    +  --copy-code-hover: var(--code-hl-bg-color, rgb(0 0 0 / 66%));
    +}
    +
    `,20);function d(D,y){const n=l("NpmBadge");return e(),o("div",null,[r,p(n,{package:"@vuepress/plugin-copy-code"}),t])}const v=a(c,[["render",d],["__file","copy-code.html.vue"]]);export{v as default}; diff --git a/assets/copy-code.html-7RjOj2Mn.js b/assets/copy-code.html-7RjOj2Mn.js new file mode 100644 index 0000000000..2db7bf34fd --- /dev/null +++ b/assets/copy-code.html-7RjOj2Mn.js @@ -0,0 +1,61 @@ +import{_ as l,r as a,o as e,c as p,a as o,b as s,e as i}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"copy-code",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#copy-code"},[s("span",null,"copy-code")])],-1),t=i(`

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

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

    使用

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

    选项

    selector

    • 类型:string | string[]

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

    • 详情:

      代码块选择器

    showInMobile

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否展示在移动端

    duration

    • 类型:number

    • 默认值:2000

    • 详情:

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

    delay

    • 类型:number

    • 默认值:800

    • 详情:

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

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

    locales

    • 类型:CopyCodeLocaleConfig

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

    • 详情:

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

    • 示例:

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

    样式

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

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: var(--code-ln-color, #9e9e9e);
    +  --copy-code-hover: var(--code-hl-bg-color, rgb(0 0 0 / 66%));
    +}
    +
    `,20);function d(D,y){const n=a("NpmBadge");return e(),p("div",null,[r,o(n,{package:"@vuepress/plugin-copy-code"}),t])}const u=l(c,[["render",d],["__file","copy-code.html.vue"]]);export{u as default}; diff --git a/assets/copy-code.html-XwXroN--.js b/assets/copy-code.html-XwXroN--.js new file mode 100644 index 0000000000..24ae8c8ee4 --- /dev/null +++ b/assets/copy-code.html-XwXroN--.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-3b611b3c","path":"/plugins/copy-code.html","title":"copy-code","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"showInMobile","slug":"showinmobile","link":"#showinmobile","children":[]},{"level":3,"title":"duration","slug":"duration","link":"#duration","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/copy-code.md"}');export{l as data}; diff --git a/assets/copy-code.html-knN80jzu.js b/assets/copy-code.html-knN80jzu.js new file mode 100644 index 0000000000..cd3b47174b --- /dev/null +++ b/assets/copy-code.html-knN80jzu.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-5666a02b","path":"/zh/plugins/copy-code.html","title":"copy-code","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"showInMobile","slug":"showinmobile","link":"#showinmobile","children":[]},{"level":3,"title":"duration","slug":"duration","link":"#duration","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706854470000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":4}]},"filePathRelative":"zh/plugins/copy-code.md"}');export{l as data}; diff --git a/assets/copyright.html-4w5HDbhl.js b/assets/copyright.html-4w5HDbhl.js new file mode 100644 index 0000000000..b8ac76ce11 --- /dev/null +++ b/assets/copyright.html-4w5HDbhl.js @@ -0,0 +1,66 @@ +import{_ as l,r as a,o as e,c as i,a as o,b as s,e as p}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"copyright",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#copyright"},[s("span",null,"copyright")])],-1),t=p(`

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

    使用

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

    启用版权信息

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

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

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

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

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

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

    禁用复制和选择

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

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

    插件选项

    author

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

    license

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

    authorGetter

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

    licenseGetter

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

    copyrightGetter

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

    triggerLength

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

    maxLength

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

    global

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

    disableCopy

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

    disableSelection

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

    canonical

    • 类型:string

    • 详情:

      首选部署位置。

    例子

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

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

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

    locales

    • 类型:CopyrightLocaleConfig

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

    • 示例:

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

    Frontmatter

    copy.triggerLength

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

    copy.maxLength

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

    copy.disableCopy

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

    copy.disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择
    `,50);function d(u,h){const n=a("NpmBadge");return e(),i("div",null,[r,o(n,{package:"@vuepress/plugin-copyright"}),t])}const g=l(c,[["render",d],["__file","copyright.html.vue"]]);export{g as default}; diff --git a/assets/copyright.html-GOVOyHFB.js b/assets/copyright.html-GOVOyHFB.js new file mode 100644 index 0000000000..eb1dd1dc3c --- /dev/null +++ b/assets/copyright.html-GOVOyHFB.js @@ -0,0 +1,66 @@ +import{_ as e,r as a,o as l,c as i,a as o,b as s,e as t}from"./app-GUhkEPRO.js";const p={},c=s("h1",{id:"copyright",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#copyright"},[s("span",null,"copyright")])],-1),r=t(`

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

    Usage

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

    This plugin is disabled globally by default, you can:

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

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

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

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

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

    Disable Copy and Selection

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

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

    Options

    author

    • Type: string
    • Details: Default author Information

    license

    • Type: string
    • Details: Default license Information

    authorGetter

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

    licenseGetter

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

    copyrightGetter

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

    triggerLength

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

    maxLength

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

    global

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

    disableCopy

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

    disableSelection

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

    canonical

    • Type: string
    • Details: Canonical deploy location.

    Example

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

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

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

    locales

    • Type: CopyrightLocaleConfig

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

    • Example:

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

    Frontmatter

    copy.triggerLength

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

    copy.maxLength

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

    copy.disableCopy

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

    copy.disableSelection

    • Type: boolean
    • Default: false
    • Details: Disable selection
    `,50);function d(u,h){const n=a("NpmBadge");return l(),i("div",null,[c,o(n,{package:"@vuepress/plugin-copyright"}),r])}const g=e(p,[["render",d],["__file","copyright.html.vue"]]);export{g as default}; diff --git a/assets/copyright.html-cTO_2UMM.js b/assets/copyright.html-cTO_2UMM.js new file mode 100644 index 0000000000..56b30032e4 --- /dev/null +++ b/assets/copyright.html-cTO_2UMM.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3aef69fa","path":"/plugins/copyright.html","title":"copyright","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Enabling Copyright","slug":"enabling-copyright","link":"#enabling-copyright","children":[]},{"level":3,"title":"Disable Copy and Selection","slug":"disable-copy-and-selection","link":"#disable-copy-and-selection","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"author","slug":"author","link":"#author","children":[]},{"level":3,"title":"license","slug":"license","link":"#license","children":[]},{"level":3,"title":"authorGetter","slug":"authorgetter","link":"#authorgetter","children":[]},{"level":3,"title":"licenseGetter","slug":"licensegetter","link":"#licensegetter","children":[]},{"level":3,"title":"copyrightGetter","slug":"copyrightgetter","link":"#copyrightgetter","children":[]},{"level":3,"title":"triggerLength","slug":"triggerlength","link":"#triggerlength","children":[]},{"level":3,"title":"maxLength","slug":"maxlength","link":"#maxlength","children":[]},{"level":3,"title":"global","slug":"global","link":"#global","children":[]},{"level":3,"title":"disableCopy","slug":"disablecopy","link":"#disablecopy","children":[]},{"level":3,"title":"disableSelection","slug":"disableselection","link":"#disableselection","children":[]},{"level":3,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"copy.triggerLength","slug":"copy-triggerlength","link":"#copy-triggerlength","children":[]},{"level":3,"title":"copy.maxLength","slug":"copy-maxlength","link":"#copy-maxlength","children":[]},{"level":3,"title":"copy.disableCopy","slug":"copy-disablecopy","link":"#copy-disablecopy","children":[]},{"level":3,"title":"copy.disableSelection","slug":"copy-disableselection","link":"#copy-disableselection","children":[]}]}],"git":{"updatedTime":1706859072000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/copyright.md"}');export{e as data}; diff --git a/assets/copyright.html-kHatrOgy.js b/assets/copyright.html-kHatrOgy.js new file mode 100644 index 0000000000..4bd9ea67f8 --- /dev/null +++ b/assets/copyright.html-kHatrOgy.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-55f4eee9","path":"/zh/plugins/copyright.html","title":"copyright","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"启用版权信息","slug":"启用版权信息","link":"#启用版权信息","children":[]},{"level":3,"title":"禁用复制和选择","slug":"禁用复制和选择","link":"#禁用复制和选择","children":[]}]},{"level":2,"title":"插件选项","slug":"插件选项","link":"#插件选项","children":[{"level":3,"title":"author","slug":"author","link":"#author","children":[]},{"level":3,"title":"license","slug":"license","link":"#license","children":[]},{"level":3,"title":"authorGetter","slug":"authorgetter","link":"#authorgetter","children":[]},{"level":3,"title":"licenseGetter","slug":"licensegetter","link":"#licensegetter","children":[]},{"level":3,"title":"copyrightGetter","slug":"copyrightgetter","link":"#copyrightgetter","children":[]},{"level":3,"title":"triggerLength","slug":"triggerlength","link":"#triggerlength","children":[]},{"level":3,"title":"maxLength","slug":"maxlength","link":"#maxlength","children":[]},{"level":3,"title":"global","slug":"global","link":"#global","children":[]},{"level":3,"title":"disableCopy","slug":"disablecopy","link":"#disablecopy","children":[]},{"level":3,"title":"disableSelection","slug":"disableselection","link":"#disableselection","children":[]},{"level":3,"title":"canonical","slug":"canonical","link":"#canonical","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"copy.triggerLength","slug":"copy-triggerlength","link":"#copy-triggerlength","children":[]},{"level":3,"title":"copy.maxLength","slug":"copy-maxlength","link":"#copy-maxlength","children":[]},{"level":3,"title":"copy.disableCopy","slug":"copy-disablecopy","link":"#copy-disablecopy","children":[]},{"level":3,"title":"copy.disableSelection","slug":"copy-disableselection","link":"#copy-disableselection","children":[]}]}],"git":{"updatedTime":1706859072000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/copyright.md"}');export{e as data}; diff --git a/assets/docsearch.html-XgUI2xOq.js b/assets/docsearch.html-XgUI2xOq.js new file mode 100644 index 0000000000..20f7636bd1 --- /dev/null +++ b/assets/docsearch.html-XgUI2xOq.js @@ -0,0 +1,203 @@ +import{_ as i,r as c,o as t,c as D,a as l,b as s,d as n,w as o,e}from"./app-GUhkEPRO.js";const d={},y=s("h1",{id:"docsearch",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#docsearch"},[s("span",null,"docsearch")])],-1),u={href:"https://docsearch.algolia.com/",target:"_blank",rel:"noopener noreferrer"},v=e(`

    提示

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

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

    使用方法

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

    获取搜索索引

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

































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     







     
































































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

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

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

    locales

    • 类型: Record<string, DocsearchPluginOptions>

    • 详情:

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

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

    • 示例:

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

    injectStyles

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否注入 DocSearch 的默认样式。

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

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

    样式

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

    组件

    Docsearch

    • 详情:

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

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

    提示

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

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

    TIP

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

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

    Usage

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

    Get Search Index

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


































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     








     
































































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

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

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

    locales

    • Type: Record<string, DocsearchPluginOptions>

    • Details:

      Options of this plugin in different locales.

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

    • Example:

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

    injectStyles

    • Type: boolean

    • Default: true

    • Details:

      Whether to inject the default styles of DocSearch or not.

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

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

    Styles

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

    Components

    Docsearch

    • Details:

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

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

    TIP

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

    `,5);function Ts(Ss,Is){const c=r("NpmBadge"),a=r("ExternalLinkIcon"),o=r("RouterLink");return t(),D("div",null,[y,l(c,{package:"@vuepress/plugin-docsearch"}),s("p",null,[n("Integrate "),s("a",u,[n("Algolia DocSearch"),l(a)]),n(" into VuePress, which can provide search to your documentation site.")]),h,s("p",null,[n("You need to "),s("a",v,[n("submit the URL of your site"),l(a)]),n(" to join the DocSearch program. The DocSearch team will send "),C,n(" and "),b,n(" to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress.")]),s("p",null,[n("Alternatively, you can "),s("a",m,[n("run your own crawler"),l(a)]),n(" to generate the index, and then use your own "),E,n(", "),_,n(" and "),g,n(" to configure this plugin.")]),A,s("div",f,[x,s("p",null,[n("If you are not using default theme, or you meet any problems when using docsearch, you can also check the above example crawler config, and ahead to "),s("a",B,[n("Algolia Crawler"),l(a)]),n(", and edit your config with 'Editor' panel in project sidebar.")])]),F,k,s("ul",null,[w,T,S,s("li",null,[I,s("ul",null,[s("li",null,[s("a",P,[n("DocSearch > Options > apiKey"),l(a)])])])])]),N,s("ul",null,[R,O,U,s("li",null,[Y,s("ul",null,[s("li",null,[s("a",z,[n("DocSearch > Options > indexName"),l(a)])])])])]),q,s("ul",null,[L,W,j,s("li",null,[K,s("ul",null,[s("li",null,[s("a",V,[n("DocSearch > Options > appId"),l(a)])])])])]),M,s("ul",null,[X,H,s("li",null,[G,s("ul",null,[s("li",null,[s("a",Q,[n("DocSearch > Options > searchParameters"),l(a)])]),s("li",null,[s("a",$,[n("Algolia > Search API Parameters"),l(a)])])])])]),J,s("ul",null,[Z,ss,ns,s("li",null,[ls,s("ul",null,[s("li",null,[s("a",as,[n("DocSearch > Options > placeholder"),l(a)])])])])]),es,s("ul",null,[os,ps,rs,s("li",null,[cs,s("ul",null,[s("li",null,[s("a",is,[n("DocSearch > Options > disableUserPersonalization"),l(a)])])])])]),ts,s("ul",null,[Ds,ds,s("li",null,[ys,s("ul",null,[s("li",null,[s("a",us,[n("DocSearch > Options > initialQuery"),l(a)])])])])]),hs,s("ul",null,[vs,Cs,s("li",null,[bs,s("ul",null,[s("li",null,[s("a",ms,[n("DocSearch > Options > translations"),l(a)])])])])]),Es,s("ul",null,[s("li",null,[n("Also see: "),s("ul",null,[s("li",null,[l(o,{to:"/guide/i18n.html"},{default:p(()=>[n("Guide > I18n")]),_:1})])])])]),_s,s("ul",null,[gs,s("li",null,[s("p",null,[n("Default: "),l(o,{to:"/config.html#base"},{default:p(()=>[n("base")]),_:1})])]),s("li",null,[As,fs,xs,s("p",null,[n("However, if the "),l(o,{to:"/config.html#base"},{default:p(()=>[n("base")]),_:1}),n(" of your deployments are different for different domains, you need to set the option to the "),l(o,{to:"/config.html#base"},{default:p(()=>[n("base")]),_:1}),n(" of your "),Bs,n(", so that other deployments could reuse the search index correctly.")])])]),Fs,s("p",null,[n("You can customize styles via CSS variables that provided by "),s("a",ks,[n("@docsearch/css"),l(a)]),n(":")]),ws])}const Ns=i(d,[["render",Ts],["__file","docsearch.html.vue"]]);export{Ns as default}; diff --git a/assets/extending-a-theme-01-OfxlnZki.js b/assets/extending-a-theme-01-OfxlnZki.js new file mode 100644 index 0000000000..0372478e41 --- /dev/null +++ b/assets/extending-a-theme-01-OfxlnZki.js @@ -0,0 +1 @@ +const e="/ecosystem/images/cookbook/extending-a-theme-01.png";export{e as _}; diff --git a/assets/extending.html-EihhNNxm.js b/assets/extending.html-EihhNNxm.js new file mode 100644 index 0000000000..5cbf246bcc --- /dev/null +++ b/assets/extending.html-EihhNNxm.js @@ -0,0 +1,65 @@ +import{_ as p}from"./extending-a-theme-01-OfxlnZki.js";import{_ as o,r as c,o as t,c as r,b as n,d as s,a as l,e}from"./app-GUhkEPRO.js";const i={},D=e(`

    继承

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

    布局插槽

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

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

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

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

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

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

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

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

    extending-a-theme

    组件替换

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

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

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

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

    开发一个子主题

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

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // 在子主题的客户端配置文件中覆盖布局
    +    // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // 覆盖组件别名
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    `,5);function b(h,E){const a=c("ExternalLinkIcon");return t(),r("div",null,[D,n("p",null,[s("默认主题将所有 "),n("a",d,[s("非全局的组件"),l(a)]),s(" 都注册了一个带 "),y,s(" 前缀的 "),n("a",C,[s("alias"),l(a)]),s(" 。例如,"),v,s(" 的别名是 "),u,s(" 。")]),m])}const g=o(i,[["render",b],["__file","extending.html.vue"]]);export{g as default}; diff --git a/assets/extending.html-SKKh8LlA.js b/assets/extending.html-SKKh8LlA.js new file mode 100644 index 0000000000..5180f6c074 --- /dev/null +++ b/assets/extending.html-SKKh8LlA.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3fd0046b","path":"/themes/default/extending.html","title":"Extending","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Layout Slots","slug":"layout-slots","link":"#layout-slots","children":[]},{"level":2,"title":"Components Replacement","slug":"components-replacement","link":"#components-replacement","children":[]},{"level":2,"title":"Developing a Child Theme","slug":"developing-a-child-theme","link":"#developing-a-child-theme","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/extending.md"}');export{e as data}; diff --git a/assets/extending.html-TWRhgpOs.js b/assets/extending.html-TWRhgpOs.js new file mode 100644 index 0000000000..84aec921c2 --- /dev/null +++ b/assets/extending.html-TWRhgpOs.js @@ -0,0 +1,66 @@ +import{_ as o}from"./extending-a-theme-01-OfxlnZki.js";import{_ as p,r as t,o as c,c as i,b as n,d as s,a,e as l}from"./app-GUhkEPRO.js";const r={},d=l(`

    Extending

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

    Layout Slots

    Default theme's Layout provides some slots:

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

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

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

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

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

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

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

    extending-a-theme

    Components Replacement

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

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

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

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

    Developing a Child Theme

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

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // override layouts in child theme's client config file
    +    // notice that you would build ts to js before publishing to npm,
    +    // so this should be the path to the js file
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // override component alias
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    `,5);function h(b,f){const e=t("ExternalLinkIcon");return c(),i("div",null,[d,n("p",null,[s("Default theme has registered "),n("a",D,[s("alias"),a(e)]),s(" for every "),n("a",y,[s("non-global components"),a(e)]),s(" with a "),u,s(" prefix. For example, the alias of "),m,s(" is "),v,s(".")]),C])}const F=p(r,[["render",h],["__file","extending.html.vue"]]);export{F as default}; diff --git a/assets/extending.html-cRARANhQ.js b/assets/extending.html-cRARANhQ.js new file mode 100644 index 0000000000..5464f475e3 --- /dev/null +++ b/assets/extending.html-cRARANhQ.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-88f236c8","path":"/zh/themes/default/extending.html","title":"继承","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"布局插槽","slug":"布局插槽","link":"#布局插槽","children":[]},{"level":2,"title":"组件替换","slug":"组件替换","link":"#组件替换","children":[]},{"level":2,"title":"开发一个子主题","slug":"开发一个子主题","link":"#开发一个子主题","children":[]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/extending.md"}');export{e as data}; diff --git a/assets/external-link-icon.html-And8gISn.js b/assets/external-link-icon.html-And8gISn.js new file mode 100644 index 0000000000..2351b27e2e --- /dev/null +++ b/assets/external-link-icon.html-And8gISn.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-1dcbe4a7","path":"/plugins/external-link-icon.html","title":"external-link-icon","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"ExternalLinkIcon","slug":"externallinkicon-1","link":"#externallinkicon-1","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/external-link-icon.md"}');export{e as data}; diff --git a/assets/external-link-icon.html-gzX-ygIZ.js b/assets/external-link-icon.html-gzX-ygIZ.js new file mode 100644 index 0000000000..d78da397d2 --- /dev/null +++ b/assets/external-link-icon.html-gzX-ygIZ.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-f16906d0","path":"/zh/plugins/external-link-icon.html","title":"external-link-icon","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"ExternalLinkIcon","slug":"externallinkicon-1","link":"#externallinkicon-1","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/external-link-icon.md"}');export{l as data}; diff --git a/assets/external-link-icon.html-pvvkRIA0.js b/assets/external-link-icon.html-pvvkRIA0.js new file mode 100644 index 0000000000..26a2291f36 --- /dev/null +++ b/assets/external-link-icon.html-pvvkRIA0.js @@ -0,0 +1,28 @@ +import{_ as t,r as s,o as c,c as r,a,b as n,d as e,w as d,e as l}from"./app-GUhkEPRO.js";const D={},u=n("h1",{id:"external-link-icon",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#external-link-icon"},[n("span",null,"external-link-icon")])],-1),h=l(`

    This plugin has been integrated into the default theme.

    Usage

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

    Options

    locales

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

    • Details:

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

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

    • Example:

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

    Frontmatter

    externalLinkIcon

    • Type: boolean

    • Details:

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

    Styles

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

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

    Components

    ExternalLinkIcon

    • Details:

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

    TIP

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

    `,10);function m(y,b){const i=s("NpmBadge"),o=s("ExternalLinkIcon"),p=s("RouterLink");return c(),r("div",null,[u,a(i,{package:"@vuepress/plugin-external-link-icon"}),n("p",null,[e("This plugin will add an icon to the external link in your markdown content, i.e. "),a(o)]),h,n("ul",null,[n("li",null,[e("Also see: "),n("ul",null,[n("li",null,[a(p,{to:"/guide/i18n.html"},{default:d(()=>[e("Guide > I18n")]),_:1})])])])]),v])}const C=t(D,[["render",m],["__file","external-link-icon.html.vue"]]);export{C as default}; diff --git a/assets/external-link-icon.html-wC4QqVHc.js b/assets/external-link-icon.html-wC4QqVHc.js new file mode 100644 index 0000000000..a546a50279 --- /dev/null +++ b/assets/external-link-icon.html-wC4QqVHc.js @@ -0,0 +1,28 @@ +import{_ as c,r as n,o as r,c as t,a,b as s,d as l,w as d,e}from"./app-GUhkEPRO.js";const D={},u=s("h1",{id:"external-link-icon",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#external-link-icon"},[s("span",null,"external-link-icon")])],-1),v=e(`

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

    使用方法

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

    配置项

    locales

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

    • 详情:

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

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

    • 示例:

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

    Frontmatter

    externalLinkIcon

    • 类型: boolean

    • 详情:

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

    样式

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

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

    组件

    ExternalLinkIcon

    • 详情:

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

    提示

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

    `,10);function m(y,b){const i=n("NpmBadge"),p=n("ExternalLinkIcon"),o=n("RouterLink");return r(),t("div",null,[u,a(i,{package:"@vuepress/plugin-external-link-icon"}),s("p",null,[l("该插件会为你 Markdown 内容中的外部链接添加一个图标,即 "),a(p)]),v,s("ul",null,[s("li",null,[l("参考: "),s("ul",null,[s("li",null,[a(o,{to:"/guide/i18n.html"},{default:d(()=>[l("指南 > 多语言支持")]),_:1})])])])]),h])}const C=c(D,[["render",m],["__file","external-link-icon.html.vue"]]);export{C as default}; diff --git a/assets/frontmatter.html-5APACrWs.js b/assets/frontmatter.html-5APACrWs.js new file mode 100644 index 0000000000..256f43857b --- /dev/null +++ b/assets/frontmatter.html-5APACrWs.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-1631bbc0","path":"/zh/plugins/sitemap/frontmatter.html","title":"Frontmatter","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"sitemap","slug":"sitemap","link":"#sitemap","children":[{"level":3,"title":"sitemap.changefreq","slug":"sitemap-changefreq","link":"#sitemap-changefreq","children":[]},{"level":3,"title":"sitemap.priority","slug":"sitemap-priority","link":"#sitemap-priority","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/frontmatter.md"}');export{e as data}; diff --git a/assets/frontmatter.html-7uMpDeX9.js b/assets/frontmatter.html-7uMpDeX9.js new file mode 100644 index 0000000000..1fac362d90 --- /dev/null +++ b/assets/frontmatter.html-7uMpDeX9.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4fbe5591","path":"/plugins/sitemap/frontmatter.html","title":"Frontmatter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"sitemap","slug":"sitemap","link":"#sitemap","children":[{"level":3,"title":"sitemap.changefreq","slug":"sitemap-changefreq","link":"#sitemap-changefreq","children":[]},{"level":3,"title":"sitemap.priority","slug":"sitemap-priority","link":"#sitemap-priority","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/sitemap/frontmatter.md"}');export{e as data}; diff --git a/assets/frontmatter.html-APRPiTDP.js b/assets/frontmatter.html-APRPiTDP.js new file mode 100644 index 0000000000..cf7a5807f9 --- /dev/null +++ b/assets/frontmatter.html-APRPiTDP.js @@ -0,0 +1,51 @@ +import{_ as s,o as n,c as a,e}from"./app-GUhkEPRO.js";const l={},i=e(`

    Frontmatter 配置

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

    添加与移除

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

    读取的 Frontmatter 信息

    title

    • 类型:string

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

    description

    • 类型:string

    页面描述

    date

    • 类型:Date

    页面的发布日期

    article

    • 类型:boolean

    该页面是否是文章

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

    • 类型:string

    页面版权信息

    cover / image / banner

    • 类型:string

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

    Frontmatter 选项

    feed.title

    • 类型:string

    Feed 项目的标题

    feed.description

    • 类型:string

    Feed 项目的描述

    feed.content

    • 类型:string

    Feed 项目的内容

    feed.author

    • 类型:FeedAuthor[] | FeedAuthor

    Feed 项目的作者

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

    feed.contributor

    • 类型:FeedContributor[] | FeedContributor

    Feed 项目的贡献者

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

    feed.guid

    • 类型:string

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

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

    `,46),p=[i];function c(r,o){return n(),a("div",null,p)}const t=s(l,[["render",c],["__file","frontmatter.html.vue"]]);export{t as default}; diff --git a/assets/frontmatter.html-cqZOVL8x.js b/assets/frontmatter.html-cqZOVL8x.js new file mode 100644 index 0000000000..5e55022a67 --- /dev/null +++ b/assets/frontmatter.html-cqZOVL8x.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-b09df90c","path":"/zh/themes/default/frontmatter.html","title":"Frontmatter","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"所有页面","slug":"所有页面","link":"#所有页面","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"pageClass","slug":"pageclass","link":"#pageclass","children":[]}]},{"level":2,"title":"首页","slug":"首页","link":"#首页","children":[{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"heroImage","slug":"heroimage","link":"#heroimage","children":[]},{"level":3,"title":"heroImageDark","slug":"heroimagedark","link":"#heroimagedark","children":[]},{"level":3,"title":"heroAlt","slug":"heroalt","link":"#heroalt","children":[]},{"level":3,"title":"heroHeight","slug":"heroheight","link":"#heroheight","children":[]},{"level":3,"title":"heroText","slug":"herotext","link":"#herotext","children":[]},{"level":3,"title":"tagline","slug":"tagline","link":"#tagline","children":[]},{"level":3,"title":"actions","slug":"actions","link":"#actions","children":[]},{"level":3,"title":"features","slug":"features","link":"#features","children":[]},{"level":3,"title":"footer","slug":"footer","link":"#footer","children":[]},{"level":3,"title":"footerHtml","slug":"footerhtml","link":"#footerhtml","children":[]}]},{"level":2,"title":"普通页面","slug":"普通页面","link":"#普通页面","children":[{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"prev","slug":"prev","link":"#prev","children":[]},{"level":3,"title":"next","slug":"next","link":"#next","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/frontmatter.md"}');export{e as data}; diff --git a/assets/frontmatter.html-cv35bZ-S.js b/assets/frontmatter.html-cv35bZ-S.js new file mode 100644 index 0000000000..714d0b03cb --- /dev/null +++ b/assets/frontmatter.html-cv35bZ-S.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3e316b89","path":"/themes/default/frontmatter.html","title":"Frontmatter","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"All Pages","slug":"all-pages","link":"#all-pages","children":[{"level":3,"title":"externalLinkIcon","slug":"externallinkicon","link":"#externallinkicon","children":[]},{"level":3,"title":"navbar","slug":"navbar","link":"#navbar","children":[]},{"level":3,"title":"pageClass","slug":"pageclass","link":"#pageclass","children":[]}]},{"level":2,"title":"Home Page","slug":"home-page","link":"#home-page","children":[{"level":3,"title":"home","slug":"home","link":"#home","children":[]},{"level":3,"title":"heroImage","slug":"heroimage","link":"#heroimage","children":[]},{"level":3,"title":"heroImageDark","slug":"heroimagedark","link":"#heroimagedark","children":[]},{"level":3,"title":"heroAlt","slug":"heroalt","link":"#heroalt","children":[]},{"level":3,"title":"heroHeight","slug":"heroheight","link":"#heroheight","children":[]},{"level":3,"title":"heroText","slug":"herotext","link":"#herotext","children":[]},{"level":3,"title":"tagline","slug":"tagline","link":"#tagline","children":[]},{"level":3,"title":"actions","slug":"actions","link":"#actions","children":[]},{"level":3,"title":"features","slug":"features","link":"#features","children":[]},{"level":3,"title":"footer","slug":"footer","link":"#footer","children":[]},{"level":3,"title":"footerHtml","slug":"footerhtml","link":"#footerhtml","children":[]}]},{"level":2,"title":"Normal Page","slug":"normal-page","link":"#normal-page","children":[{"level":3,"title":"editLink","slug":"editlink","link":"#editlink","children":[]},{"level":3,"title":"editLinkPattern","slug":"editlinkpattern","link":"#editlinkpattern","children":[]},{"level":3,"title":"lastUpdated","slug":"lastupdated","link":"#lastupdated","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]},{"level":3,"title":"sidebar","slug":"sidebar","link":"#sidebar","children":[]},{"level":3,"title":"sidebarDepth","slug":"sidebardepth","link":"#sidebardepth","children":[]},{"level":3,"title":"prev","slug":"prev","link":"#prev","children":[]},{"level":3,"title":"next","slug":"next","link":"#next","children":[]}]}],"git":{"updatedTime":1706625181000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/frontmatter.md"}');export{e as data}; diff --git a/assets/frontmatter.html-e8IdhucI.js b/assets/frontmatter.html-e8IdhucI.js new file mode 100644 index 0000000000..2406a03756 --- /dev/null +++ b/assets/frontmatter.html-e8IdhucI.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5a3bfbe9","path":"/zh/plugins/feed/frontmatter.html","title":"Frontmatter 配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"添加与移除","slug":"添加与移除","link":"#添加与移除","children":[]},{"level":2,"title":"读取的 Frontmatter 信息","slug":"读取的-frontmatter-信息","link":"#读取的-frontmatter-信息","children":[{"level":3,"title":"title","slug":"title","link":"#title","children":[]},{"level":3,"title":"description","slug":"description","link":"#description","children":[]},{"level":3,"title":"date","slug":"date","link":"#date","children":[]},{"level":3,"title":"article","slug":"article","link":"#article","children":[]},{"level":3,"title":"copyright","slug":"copyright","link":"#copyright","children":[]},{"level":3,"title":"cover / image / banner","slug":"cover-image-banner","link":"#cover-image-banner","children":[]}]},{"level":2,"title":"Frontmatter 选项","slug":"frontmatter-选项","link":"#frontmatter-选项","children":[{"level":3,"title":"feed.title","slug":"feed-title","link":"#feed-title","children":[]},{"level":3,"title":"feed.description","slug":"feed-description","link":"#feed-description","children":[]},{"level":3,"title":"feed.content","slug":"feed-content","link":"#feed-content","children":[]},{"level":3,"title":"feed.author","slug":"feed-author","link":"#feed-author","children":[]},{"level":3,"title":"feed.contributor","slug":"feed-contributor","link":"#feed-contributor","children":[]},{"level":3,"title":"feed.guid","slug":"feed-guid","link":"#feed-guid","children":[]}]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/frontmatter.md"}');export{e as data}; diff --git a/assets/frontmatter.html-mMjdstQ1.js b/assets/frontmatter.html-mMjdstQ1.js new file mode 100644 index 0000000000..97650f27ca --- /dev/null +++ b/assets/frontmatter.html-mMjdstQ1.js @@ -0,0 +1,60 @@ +import{_ as r,r as t,o as c,c as d,a as n,b as l,d as s,w as a,e as i}from"./app-GUhkEPRO.js";const u={},h=l("h1",{id:"frontmatter",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#frontmatter"},[l("span",null,"Frontmatter")])],-1),D=l("h2",{id:"所有页面",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#所有页面"},[l("span",null,"所有页面")])],-1),m=l("p",null,"本章节中的 Frontmatter 会在所有类型的页面中生效。",-1),v=l("h3",{id:"externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#externallinkicon"},[l("span",null,"externalLinkIcon")])],-1),b=l("li",null,[l("p",null,[s("类型: "),l("code",null,"boolean")])],-1),_=l("p",null,"详情:",-1),y=l("p",null,"参考:",-1),g=l("h3",{id:"navbar",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#navbar"},[l("span",null,"navbar")])],-1),f=l("li",null,[l("p",null,[s("类型: "),l("code",null,"boolean")])],-1),k=l("li",null,[l("p",null,"详情:"),l("p",null,"是否在当前页面展示导航栏。"),l("p",null,"如果你在主题配置中禁用了导航栏,那么该 Frontmatter 将不会生效。")],-1),C=l("p",null,"参考:",-1),x=i(`

    pageClass

    • 类型: string

    • 详情:

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

    • 示例:

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

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

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

    首页

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

    home

    • 类型: boolean

    • 详情:

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

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

    • 示例:

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

    heroImage

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

  • 详情:

    首页图片的 URL 。

  • 示例:

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

    heroAlt

    • 类型: string

    • 详情:

      首页图片的 alt 属性。

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

    heroHeight

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

    actions

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

      配置首页按钮。

    • 示例:

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

    features

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

      配置首页特性列表。

    • 示例:

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

    • 详情:

      首页的页脚。

    footerHtml

    • 类型: boolean

    • 详情:

      是否允许页脚中使用 HTML 。

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

    普通页面

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

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

    prev

    • 类型: NavLink | string

    • 详情:

      上一个页面的链接。

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

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

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

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

    next

    • 类型: NavLink | string

    • 详情:

      下一个页面的链接。

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

      类型和 prev Frontmatter 相同。

    `,5);function xl(El,Ll){const p=t("NpmBadge"),e=t("RouterLink"),o=t("ExternalLinkIcon");return c(),d("div",null,[h,n(p,{package:"@vuepress/theme-default"}),D,m,v,l("ul",null,[b,l("li",null,[_,l("p",null,[s("由"),n(e,{to:"/zh/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[s("@vuepress/plugin-external-link-icon")]),_:1}),s(" 提供。")])]),l("li",null,[y,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#themeplugins-externallinkicon"},{default:a(()=>[s("默认主题 > 配置 > themePlugins.externalLinkIcon")]),_:1})])])])]),g,l("ul",null,[f,k,l("li",null,[C,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#navbar"},{default:a(()=>[s("默认主题 > 配置 > navbar")]),_:1})])])])]),x,l("ul",null,[l("li",null,[s("参考: "),l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/styles.html#style-%E6%96%87%E4%BB%B6"},{default:a(()=>[s("默认主题 > 样式 > Style 文件")]),_:1})])])])]),E,l("ul",null,[L,l("li",null,[A,l("ul",null,[l("li",null,[l("a",B,[s("指南 > 静态资源 > Public 文件"),n(o)])])])])]),z,l("ul",null,[F,N,l("li",null,[w,l("ul",null,[I,l("li",null,[n(e,{to:"/zh/themes/default/config.html#colormode"},{default:a(()=>[s("默认主题 > 配置 > colorMode")]),_:1})])])])]),S,l("ul",null,[V,P,l("li",null,[H,M,R,l("p",null,[s("需要注意的是,首页图片的高度同样受到了 CSS 的约束。设置这个属性主要是为了减少由加载首页图片引起的 "),l("a",T,[s("累积布局偏移 (CLS)"),n(o)]),s(" 。")])])]),U,l("ul",null,[j,l("li",null,[G,O,l("p",null,[s("如果不设置,则默认使用站点 "),l("a",q,[s("title"),n(o)]),s(" 。")]),J])]),K,l("ul",null,[Q,l("li",null,[W,X,l("p",null,[s("如果不设置,则默认使用站点 "),l("a",Y,[s("description"),n(o)]),s(" 。")]),Z])]),$,l("ul",null,[ll,sl,l("li",null,[nl,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#editlink"},{default:a(()=>[s("默认主题 > 配置 > editLink")]),_:1})])])])]),el,l("ul",null,[al,il,l("li",null,[ol,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#editlinkpattern"},{default:a(()=>[s("默认主题 > 配置 > editLinkPattern")]),_:1})])])])]),tl,l("ul",null,[pl,rl,l("li",null,[cl,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#lastupdated"},{default:a(()=>[s("默认主题 > 配置 > lastUpdated")]),_:1})])])])]),dl,l("ul",null,[ul,hl,l("li",null,[Dl,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#contributors"},{default:a(()=>[s("默认主题 > 配置 > contributors")]),_:1})])])])]),ml,l("ul",null,[vl,bl,l("li",null,[_l,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#sidebar"},{default:a(()=>[s("默认主题 > 配置 > sidebar")]),_:1})])])])]),yl,l("ul",null,[gl,fl,l("li",null,[kl,l("ul",null,[l("li",null,[n(e,{to:"/zh/themes/default/config.html#sidebardepth"},{default:a(()=>[s("默认主题 > 配置 > sidebarDepth")]),_:1})])])])]),Cl])}const Bl=r(u,[["render",xl],["__file","frontmatter.html.vue"]]);export{Bl as default}; diff --git a/assets/frontmatter.html-onFKhKev.js b/assets/frontmatter.html-onFKhKev.js new file mode 100644 index 0000000000..baa8616483 --- /dev/null +++ b/assets/frontmatter.html-onFKhKev.js @@ -0,0 +1 @@ +import{_ as o,r as l,o as s,c as i,b as e,d as a,a as r,w as c,e as t}from"./app-GUhkEPRO.js";const p={},d=t('

    Frontmatter

    sitemap

    • 类型:SitemapFrontmatterOptions | false

    • 详情:

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

    sitemap.changefreq

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

    sitemap.priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    ',2);function f(y,x){const n=l("RouterLink");return s(),i("div",null,[d,e("ul",null,[h,m,e("li",null,[u,e("p",null,[a("页面默认更新频率。它会覆盖插件选项中的 "),r(n,{to:"/zh/plugins/sitemap/config.html#changefreq"},{default:c(()=>[a("changefreq")]),_:1}),a(" 选项。")])])]),_])}const b=o(p,[["render",f],["__file","frontmatter.html.vue"]]);export{b as default}; diff --git a/assets/frontmatter.html-r9qGaG_A.js b/assets/frontmatter.html-r9qGaG_A.js new file mode 100644 index 0000000000..962abfe9ec --- /dev/null +++ b/assets/frontmatter.html-r9qGaG_A.js @@ -0,0 +1 @@ +import{_ as n,r as o,o as i,c as r,b as e,d as a,a as s,w as p,e as t}from"./app-GUhkEPRO.js";const c={},d=t('

    Frontmatter

    sitemap

    • Type: SitemapFrontmatterOptions | false

    • Details:

      false means exclude the page from sitemap.

    sitemap.changefreq

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

    sitemap.priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, range from 0 to 1.

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

    pageClass

    • Type: string

    • Details:

      Add extra class name to this page.

    • Example:

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

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

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

    Home Page

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

    home

    • Type: boolean

    • Details:

      Specify whether the page is homepage or a normal page.

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

    • Example:

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

    heroImage

    • Type: string

    • Details:

      Specify the url of the hero image.

    • Example:

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

    heroAlt

    • Type: string

    • Details:

      Specify the alt attribute of the hero image.

      This will fallback to the heroText.

    heroHeight

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

    actions

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

      Configuration of the action buttons.

    • Example:

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

    features

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

      Configuration of the features list.

    • Example:

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

    • Details:

      Specify the content of the footer.

    footerHtml

    • Type: boolean

    • Details:

      Allow HTML in footer or not.

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

    Normal Page

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

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

    prev

    • Type: NavLink | string

    • Details:

      Specify the link of the previous page.

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

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

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

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

    next

    • Type: NavLink | string

    • Details:

      Specify the link of the next page.

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

      The type is the same as prev frontmatter.

    `,5);function ke(Ce,xe){const p=o("NpmBadge"),n=o("RouterLink"),i=o("ExternalLinkIcon");return c(),d("div",null,[h,s(p,{package:"@vuepress/theme-default"}),D,m,f,e("ul",null,[y,e("li",null,[v,e("p",null,[l("Provided by "),s(n,{to:"/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[l("@vuepress/plugin-external-link-icon")]),_:1}),l(".")])]),e("li",null,[b,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#themeplugins-externallinkicon"},{default:a(()=>[l("Default Theme > Config Reference > themePlugins.externalLinkIcon")]),_:1})])])])]),g,e("ul",null,[_,k,e("li",null,[C,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#navbar"},{default:a(()=>[l("Default Theme > Config > navbar")]),_:1})])])])]),x,e("ul",null,[e("li",null,[l("Also see: "),e("ul",null,[e("li",null,[s(n,{to:"/themes/default/styles.html#style-file"},{default:a(()=>[l("Default Theme > Styles > Style File")]),_:1})])])])]),E,e("ul",null,[e("li",null,[l("Also see: "),e("ul",null,[e("li",null,[e("a",T,[l("Guide > Assets > Public Files"),s(i)])])])])]),A,e("ul",null,[w,S,e("li",null,[L,e("ul",null,[I,e("li",null,[s(n,{to:"/themes/default/config.html#colormode"},{default:a(()=>[l("Default Theme > Config > colorMode")]),_:1})])])])]),N,e("ul",null,[F,P,e("li",null,[j,B,V,e("p",null,[l("Notice that the height is also constrained by CSS. This attribute is to reduce "),e("a",H,[l("Cumulative Layout Shift (CLS)"),s(i)]),l(" that caused by the loading of the hero image.")])])]),M,e("ul",null,[G,e("li",null,[R,U,e("p",null,[l("This will fallback to the site "),e("a",Y,[l("title"),s(i)]),l(".")]),z])]),O,e("ul",null,[q,e("li",null,[J,K,e("p",null,[l("This will fallback to the site "),e("a",Q,[l("description"),s(i)]),l(".")]),W])]),X,e("ul",null,[Z,$,e("li",null,[ee,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#editlink"},{default:a(()=>[l("Default Theme > Config > editLink")]),_:1})])])])]),le,e("ul",null,[se,ne,e("li",null,[ae,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#editlinkpattern"},{default:a(()=>[l("Default Theme > Config > editLinkPattern")]),_:1})])])])]),te,e("ul",null,[ie,oe,e("li",null,[pe,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#lastupdated"},{default:a(()=>[l("Default Theme > Config > lastUpdated")]),_:1})])])])]),re,e("ul",null,[ce,de,e("li",null,[ue,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#contributors"},{default:a(()=>[l("Default Theme > Config > contributors")]),_:1})])])])]),he,e("ul",null,[De,me,e("li",null,[fe,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#sidebar"},{default:a(()=>[l("Default Theme > Config > sidebar")]),_:1})])])])]),ye,e("ul",null,[ve,be,e("li",null,[ge,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#sidebardepth"},{default:a(()=>[l("Default Theme > Config > sidebarDepth")]),_:1})])])])]),_e])}const Te=r(u,[["render",ke],["__file","frontmatter.html.vue"]]);export{Te as default}; diff --git a/assets/frontmatter.html-zRyVb7je.js b/assets/frontmatter.html-zRyVb7je.js new file mode 100644 index 0000000000..8429e8ab60 --- /dev/null +++ b/assets/frontmatter.html-zRyVb7je.js @@ -0,0 +1,51 @@ +import{_ as s,o as n,c as a,e}from"./app-GUhkEPRO.js";const l={},i=e(`

    Frontmatter Config

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

    Additions and Removals

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

    Frontmatter Information

    title

    • Type: string

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

    description

    • Type: string

    Description of the page

    date

    • Type: Date

    Date when the page was published

    article

    • Type: boolean

    Whether the page is an article

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

    • Type: string

    Page copyright information

    cover / image / banner

    • Type: string

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

    Frontmatter Options

    feed.title

    • Type: string

    The title of the feed item

    feed.description

    • Type: string

    Description of the feed item

    feed.content

    • Type: string

    The content of the feed item

    feed.author

    • Type: FeedAuthor[] | FeedAuthor

    The author of the feed item

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

    feed.contributor

    • Type: FeedContributor[] | FeedContributor

    Contributors to feed item

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

    feed.guid

    • Type: string

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

    You should ensure every feed has a unique guid.

    `,46),p=[i];function o(t,r){return n(),a("div",null,p)}const d=s(l,[["render",o],["__file","frontmatter.html.vue"]]);export{d as default}; diff --git a/assets/getter.html-AJzL42Au.js b/assets/getter.html-AJzL42Au.js new file mode 100644 index 0000000000..c5c8564a7d --- /dev/null +++ b/assets/getter.html-AJzL42Au.js @@ -0,0 +1,91 @@ +import{_ as s,o as n,c as a,e}from"./app-GUhkEPRO.js";const l={},i=e(`

    Feed 获取器

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

    getter.title

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

    项目标题获取器

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

    项目链接获取器

    getter.description

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

    项目描述获取器

    提示

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

    getter.content

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

    项目内容获取器

    getter.author

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

    项目作者获取器。

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

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

    getter.category

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

    项目分类获取器。

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

    getter.enclosure

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

    项目附件获取器。

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

    getter.publishDate

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

    项目发布日期获取器

    getter.lastUpdateDate

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

    项目最后更新日期获取器

    getter.image

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

    项目图片获取器

    确保返回一个完整的 URL。

    getter.contributor

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

    项目贡献者获取器

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

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

    项目版权获取器

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

    Feed Getter

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

    getter.title

    • Type: (page: Page) => string

    Item title getter

    • Type: (page: Page) => string

    Item link getter

    getter.description

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

    Item description getter

    TIP

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

    getter.content

    • Type: (page: Page) => string

    Item content getter

    getter.author

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

    Item author getter.

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

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

    getter.category

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

    Item category getter.

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

    getter.enclosure

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

    Item enclosure getter.

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

    getter.publishDate

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

    Item release date getter

    getter.lastUpdateDate

    • Type: (page: Page) => Date

    Item last update date getter

    getter.image

    • Type: (page: Page) => string

    Item Image Getter

    Ensure it's returning a full URL

    getter.contributor

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

    Item Contributor Getter

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

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

    Item copyright getter

    `,46),p=[i];function t(r,c){return n(),a("div",null,p)}const d=s(l,[["render",t],["__file","getter.html.vue"]]);export{d as default}; diff --git a/assets/git.html-2parGuCs.js b/assets/git.html-2parGuCs.js new file mode 100644 index 0000000000..5fc27739e3 --- /dev/null +++ b/assets/git.html-2parGuCs.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-1db67204","path":"/zh/plugins/git.html","title":"git","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"Git 仓库","slug":"git-仓库","link":"#git-仓库","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"createdTime","slug":"createdtime","link":"#createdtime","children":[]},{"level":3,"title":"updatedTime","slug":"updatedtime","link":"#updatedtime","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"gitInclude","slug":"gitinclude","link":"#gitinclude","children":[]}]},{"level":2,"title":"页面数据","slug":"页面数据","link":"#页面数据","children":[{"level":3,"title":"git.createdTime","slug":"git-createdtime","link":"#git-createdtime","children":[]},{"level":3,"title":"git.updatedTime","slug":"git-updatedtime","link":"#git-updatedtime","children":[]},{"level":3,"title":"git.contributors","slug":"git-contributors","link":"#git-contributors","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/git.md"}');export{t as data}; diff --git a/assets/git.html-72XjuwoH.js b/assets/git.html-72XjuwoH.js new file mode 100644 index 0000000000..f71a71ae59 --- /dev/null +++ b/assets/git.html-72XjuwoH.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-561922cf","path":"/plugins/git.html","title":"git","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Git Repository","slug":"git-repository","link":"#git-repository","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"createdTime","slug":"createdtime","link":"#createdtime","children":[]},{"level":3,"title":"updatedTime","slug":"updatedtime","link":"#updatedtime","children":[]},{"level":3,"title":"contributors","slug":"contributors","link":"#contributors","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"gitInclude","slug":"gitinclude","link":"#gitinclude","children":[]}]},{"level":2,"title":"Page Data","slug":"page-data","link":"#page-data","children":[{"level":3,"title":"git.createdTime","slug":"git-createdtime","link":"#git-createdtime","children":[]},{"level":3,"title":"git.updatedTime","slug":"git-updatedtime","link":"#git-updatedtime","children":[]},{"level":3,"title":"git.contributors","slug":"git-contributors","link":"#git-contributors","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/git.md"}');export{e as data}; diff --git a/assets/git.html-gfEIsrAW.js b/assets/git.html-gfEIsrAW.js new file mode 100644 index 0000000000..9a13fe6c93 --- /dev/null +++ b/assets/git.html-gfEIsrAW.js @@ -0,0 +1,30 @@ +import{_ as c,r as e,o as r,c as d,a as n,b as a,d as s,w as i,e as t}from"./app-GUhkEPRO.js";const u={},D=a("h1",{id:"git",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#git"},[a("span",null,"git")])],-1),h=a("p",null,"该插件会收集你的页面的 Git 信息,包括创建和更新时间、贡献者等。",-1),m=t(`

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

    使用方法

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

    Git 仓库

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

    注意

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

    配置项

    createdTime

    • 类型: boolean

    • 默认值: true

    • 详情:

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

    updatedTime

    • 类型: boolean

    • 默认值: true

    • 详情:

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

    contributors

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的贡献者。

    Frontmatter

    gitInclude

    • 类型: string[]

    • 详情:

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

    • 示例:

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

    页面数据

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

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

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

    git.createdTime

    • 类型: number

    • 详情:

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

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

    git.updatedTime

    • 类型: number

    • 详情:

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

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

    git.contributors

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

      页面的贡献者信息。

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

    `,24);function g(C,f){const o=e("NpmBadge"),l=e("RouterLink"),p=e("ExternalLinkIcon");return r(),d("div",null,[D,n(o,{package:"@vuepress/plugin-git"}),h,a("p",null,[s("默认主题的 "),n(l,{to:"/zh/default-theme/config.html#lastupdated"},{default:i(()=>[s("lastUpdated")]),_:1}),s(" 和 "),n(l,{to:"/zh/default-theme/config.html#contributors"},{default:i(()=>[s("contributors")]),_:1}),s(" 就是由该插件支持的。")]),m,a("p",null,[s("该插件要求你的项目在 "),a("a",v,[s("Git 仓库"),n(p)]),s(" 下,这样它才能从提交历史记录中收集信息。")]),a("p",null,[s("在构建站点时,你应该确保所有的提交记录是可以获取到的。举例来说, CI 工作流通常会在克隆你的仓库时添加 "),a("a",y,[s("--depth 1"),n(p)]),s(" 参数来避免拉取全部的提交记录,因此你需要禁用这个功能,以便该插件在 CI 可以中正常使用。")]),b])}const x=c(u,[["render",g],["__file","git.html.vue"]]);export{x as default}; diff --git a/assets/git.html-j8z7sJkh.js b/assets/git.html-j8z7sJkh.js new file mode 100644 index 0000000000..2c50af7071 --- /dev/null +++ b/assets/git.html-j8z7sJkh.js @@ -0,0 +1,30 @@ +import{_ as r,r as n,o as c,c as d,a,b as e,d as s,w as i,e as o}from"./app-GUhkEPRO.js";const u={},D=e("h1",{id:"git",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git"},[e("span",null,"git")])],-1),h=e("p",null,"This plugin will collect git information of your pages, including the created and updated time, the contributors, etc.",-1),m=o(`

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

    Usage

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

    Git Repository

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

    WARNING

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

    Options

    createdTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page created time or not.

    updatedTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page updated time or not.

    contributors

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page contributors or not.

    Frontmatter

    gitInclude

    • Type: string[]

    • Details:

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

    • Example:

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

    Page Data

    This plugin will add a git field to page data.

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

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

    git.createdTime

    • Type: number

    • Details:

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

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

    git.updatedTime

    • Type: number

    • Details:

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

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

    git.contributors

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

      The contributors information of the page.

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

    `,24);function v(f,C){const p=n("NpmBadge"),l=n("RouterLink"),t=n("ExternalLinkIcon");return c(),d("div",null,[D,a(p,{package:"@vuepress/plugin-git"}),h,e("p",null,[s("The "),a(l,{to:"/default-theme/config.html#lastupdated"},{default:i(()=>[s("lastUpdated")]),_:1}),s(" and "),a(l,{to:"/default-theme/config.html#contributors"},{default:i(()=>[s("contributors")]),_:1}),s(" of default theme is powered by this plugin.")]),m,e("p",null,[s("This plugin requires your project to be inside a "),e("a",g,[s("Git Repository"),a(t)]),s(", so that it can collect information from the commit history.")]),e("p",null,[s("You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with "),e("a",y,[s("--depth 1"),a(t)]),s(" to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI.")]),b])}const x=r(u,[["render",v],["__file","git.html.vue"]]);export{x as default}; diff --git a/assets/google-analytics.html-0yWoZfLM.js b/assets/google-analytics.html-0yWoZfLM.js new file mode 100644 index 0000000000..7300d4edef --- /dev/null +++ b/assets/google-analytics.html-0yWoZfLM.js @@ -0,0 +1,26 @@ +import{_ as i,r as o,o as c,c as r,a as e,b as s,d as n,e as a}from"./app-GUhkEPRO.js";const p={},d=s("h1",{id:"google-analytics",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#google-analytics"},[s("span",null,"google-analytics")])],-1),u={href:"https://analytics.google.com/",target:"_blank",rel:"noopener noreferrer"},D={href:"https://developers.google.com/analytics/devguides/collection/gtagjs",target:"_blank",rel:"noopener noreferrer"},g={href:"https://support.google.com/analytics/answer/10089681",target:"_blank",rel:"noopener noreferrer"},h=a(`

    Usage

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

    Reporting Events

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

    debug

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

    使用方法

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

    上报事件

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

    debug

    `,2),F=s("li",null,[s("p",null,[n("类型: "),s("code",null,"boolean")])],-1),V=s("p",null,"详情:",-1),N=s("code",null,"true",-1),B={href:"https://support.google.com/analytics/answer/7201382",target:"_blank",rel:"noopener noreferrer"},P=s("li",null,[s("p",null,"示例:")],-1),M=a(`
    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    `,1);function j(L,T){const i=o("NpmBadge"),l=o("ExternalLinkIcon");return c(),p("div",null,[d,e(i,{package:"@vuepress/plugin-google-analytics"}),s("p",null,[n("将 "),s("a",u,[n("Google Analytics"),e(l)]),n(" 集成到 VuePress 中。")]),s("p",null,[n("该插件会通过引入 "),s("a",D,[n("gtag.js"),e(l)]),n(" 来启用 "),s("a",g,[n("Google Analytics 4"),e(l)]),n(" 。")]),h,s("p",null,[n("Google Analytics 会 "),s("a",v,[n("自动收集部分事件"),e(l)]),n(" ,比如 "),y,n(", "),_,n(" 等。")]),b,s("p",null,[n("在引入该插件之后,一个全局的 "),m,n(" 函数会被挂载到 "),C,n(" 对象上,你可以使用它进行 "),s("a",f,[n("自定义事件的上报"),e(l)]),n(" 。")]),E,k,s("ul",null,[x,s("li",null,[A,X,s("p",null,[n("你可以通过 "),s("a",w,[n("这里"),e(l)]),n(' 的指引来找到你的 Measurement ID 。注意区分 Google Analytics 4 的 Measurement ID (即 "G-" 开头的 ID) 和 Universal Analytics 的 Tracking ID (即 "UA-" 开头的 ID)。')])]),G]),I,s("ul",null,[F,s("li",null,[V,s("p",null,[n("设置为 "),N,n(" 可以向 DebugView 发送事件。"),s("a",B,[n("了解更多关于 DebugView 的信息"),e(l)]),n(" 。")])]),P]),M])}const S=t(r,[["render",j],["__file","google-analytics.html.vue"]]);export{S as default}; diff --git a/assets/google-analytics.html-U0NbYbPH.js b/assets/google-analytics.html-U0NbYbPH.js new file mode 100644 index 0000000000..a60fbd4a78 --- /dev/null +++ b/assets/google-analytics.html-U0NbYbPH.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-190944c2","path":"/plugins/google-analytics.html","title":"google-analytics","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Reporting Events","slug":"reporting-events","link":"#reporting-events","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"id","slug":"id","link":"#id","children":[]},{"level":3,"title":"debug","slug":"debug","link":"#debug","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/google-analytics.md"}');export{e as data}; diff --git a/assets/guide.html-2sIkuS_O.js b/assets/guide.html-2sIkuS_O.js new file mode 100644 index 0000000000..0dfa8ce402 --- /dev/null +++ b/assets/guide.html-2sIkuS_O.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-49ab5eee","path":"/zh/plugins/sitemap/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"控制 Sitemap 链接","slug":"控制-sitemap-链接","link":"#控制-sitemap-链接","children":[]},{"level":2,"title":"输出位置","slug":"输出位置","link":"#输出位置","children":[]},{"level":2,"title":"更新周期","slug":"更新周期","link":"#更新周期","children":[]},{"level":2,"title":"优先级","slug":"优先级","link":"#优先级","children":[]},{"level":2,"title":"修改时间获取","slug":"修改时间获取","link":"#修改时间获取","children":[]},{"level":2,"title":"Sitemap 介绍","slug":"sitemap-介绍","link":"#sitemap-介绍","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/guide.md"}');export{e as data}; diff --git a/assets/guide.html-7nU4vNh-.js b/assets/guide.html-7nU4vNh-.js new file mode 100644 index 0000000000..f6ad00f240 --- /dev/null +++ b/assets/guide.html-7nU4vNh-.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-71cf31d7","path":"/zh/plugins/feed/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]},{"level":2,"title":"可读的预览","slug":"可读的预览","link":"#可读的预览","children":[]},{"level":2,"title":"频道设置","slug":"频道设置","link":"#频道设置","children":[]},{"level":2,"title":"Feed 生成","slug":"feed-生成","link":"#feed-生成","children":[{"level":3,"title":"多语言配置","slug":"多语言配置","link":"#多语言配置","children":[]}]}],"git":{"updatedTime":1706674769000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/feed/guide.md"}');export{e as data}; diff --git a/assets/guide.html-DJ-MSZJF.js b/assets/guide.html-DJ-MSZJF.js new file mode 100644 index 0000000000..966c83805a --- /dev/null +++ b/assets/guide.html-DJ-MSZJF.js @@ -0,0 +1,9 @@ +import{_ as e,o as s,c as a,e as n}from"./app-GUhkEPRO.js";const o={},l=n(`

    指南

    本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。

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

    控制 Sitemap 链接

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

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

    如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths 选项。你也可以在对应页面的 frontmatter 中,设置 sitemapfalse

    输出位置

    你还可以通过插件的 sitemapFilename 选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml

    更新周期

    页面默认的更新周期是 daily (每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq 。你也可以在页面的 frontmatter 中设置 sitemap.changefreq,页面具有更高的优先级。

    合法的频率有:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    优先级

    你可以在插件中设置 priority 以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority 来为每个页面设置优先级。可接受的值为 01 的浮点数。

    修改时间获取

    你可以通过插件的 modifyTimeGetter 来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。

    以下是一个基于文件最后修改时间的例子。

    // 基于文件最后修改时间
    +({
    +  modifyTimeGetter: (page, app) =>
    +    fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap 介绍

    网站地图 (Sitemap) 提供搜索引擎优化 (SEO):

    • 为搜索引擎爬虫提供可以浏览整个网站的链接;
    • 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面;
    • 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。

    网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。

    大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。

    Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。

    同步配置 robots.txt

    由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public 文件夹下放置了有效的 robots.txt,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)

    User-agent: *
    +
    +Allow: /
    +
    `,26),p=[l];function t(c,i){return s(),a("div",null,p)}const r=e(o,[["render",t],["__file","guide.html.vue"]]);export{r as default}; diff --git a/assets/guide.html-DKfq4qCI.js b/assets/guide.html-DKfq4qCI.js new file mode 100644 index 0000000000..631e15a25a --- /dev/null +++ b/assets/guide.html-DKfq4qCI.js @@ -0,0 +1,9 @@ +import{_ as e,o as a,c as t,e as s}from"./app-GUhkEPRO.js";const o={},n=s(`

    Guide

    This plugin will automatically generate a Sitemap for your site. To let this plugin work, you need to pass the deployed domain name to the hostname option of the plugin. If you want to preview in devServer, set devServer options.

    The plugin will automatically generate the last update time of the page based on the Git timestamp of the page, and will also declare the alternative links of the page in other languages according to the locales' config.

    By default, all site links except 404 page will be added to the Sitemap.

    To add other pages to the Sitemap outside the VuePress project page, please turn them into an array and pass to the extraUrls plugin option.

    If you don't want certain pages to appear in the sitemap, you can turn their paths into an array and pass to the excludePaths plugin option, or set sitemap to false in the frontmatter of the corresponding page.

    Output Location

    You can also control the output link through the sitemapFilename option of the plugin, the link is relative to output directory. By default, the plugin will use sitemap.xml.

    Change Frequency

    The default update cycle of the page is daily (every day). To modify the entire page cycle, please set changefreq in the plugin options. You can also set sitemap.changefreq in the frontmatter of the page. Note that page has a higher priority.

    The legal frequencies are:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    Priority

    You can set priority in the plugin to provide a default value. At the same time you can set the priority for each page through sitemap.priority in frontmatter. Acceptable values are floating point numbers from 0 to 1.

    Modify Time

    You can use option modifyTimeGetter to return a time in ISO string format, which is generated by the Git plugin by default.

    The following is an example based on the last modification time of a file.

    // Based on file last modified time
    +({
    +   modifyTimeGetter: (page, app) =>
    +     fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap Intro

    Sitemaps provide SEO (Search Engine Optimization):

    • Provide search engine spiders with links of the entire site;
    • Provide some links for search engine spiders to dynamic pages or pages that are difficult to reach by other methods;
    • If a visitor attempts to access a URL that does not exist within the site's domain, the visitor will be directed to a "file not found" error page, and the sitemap can be used as a navigation page.

    A sitemap enhances SEO by making all pages findable.

    Most search engines only follow a limited number of links within a page, so when the site is very large, a sitemap becomes essential to make everything on the site accessible to search engines and visitors.

    Sitemaps is a protocol for site administrators to publish pages that can be crawled on a site to search engine spiders. The content of sitemap files must follow the definition in XML format. Each URL can contain the update period and last update time, the priority of the URL across the site. This allows search engines to crawl site content better and more efficiently.

    Together with robots.txt

    Sitemap is basically used by search engines, when using this plugin, you'd better ensure that you have a valid robots.txt in the .vuepress/public directory to allow search engines spiders to visit your site. The simplest robots.txt is as follows (allow all search engines to access all paths)

    User-agent: *
    +
    +Allow: /
    +
    `,26),i=[n];function l(p,r){return a(),t("div",null,i)}const d=e(o,[["render",l],["__file","guide.html.vue"]]);export{d as default}; diff --git a/assets/guide.html-HoiurzPe.js b/assets/guide.html-HoiurzPe.js new file mode 100644 index 0000000000..ea6dffe428 --- /dev/null +++ b/assets/guide.html-HoiurzPe.js @@ -0,0 +1,37 @@ +import{_ as p,r as a,o as r,c as i,b as e,d as s,a as n,w as l,e as o}from"./app-GUhkEPRO.js";const D={},g=e("h1",{id:"指南",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#指南"},[e("span",null,"指南")])],-1),h=e("code",null,"",-1),y={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},m=o(`

    开箱即用

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

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

    默认的 OGP 生成逻辑

    属性名称
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || 自动生成 (当插件选项中的 autoDescriptiontrue 时)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image || 页面的第一张图片|| 插件选项的 fallbackImage
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternatesiteData.locales 包含的其他语言
    twitter:card"summary_large_image" (仅在找到图片时)
    twitter:image:altpage.title (仅在找到图片时)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    默认的 JSON-LD 生成逻辑

    属性名
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    image页面中的图片|| options.hostname + page.frontmatter.image
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    直接添加 head 标签

    你可以在页面的 frontmatter 中配置 head 选项,自主添加特定标签到页面 <head> 以增强 SEO。

    如:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    会自动注入 <meta name="keywords" content="SEO plugin" />

    自定义生成过程

    本插件也支持你完全控制生成逻辑。

    页面类型

    对于大多数页面,基本只有文章和网页两种类型,所以插件提供了 isArticle 选项让你提供辨别文章的逻辑。

    选项接受一个 (page: Page) => boolean 格式的函数,默认情况下从 Markdown 文件生成的非主页页面都会被视为文章。

    提示

    如果某个网页的确符合图书、音乐之类的“冷门”类型,你可以通过设置下方三个选项处理它们。

    OGP

    你可以使用插件选项的 ogp 传入一个函数来按照你的需要修改默认 OGP 对象并返回。

    function ogp(
    +  /** 插件推断的 OGP 信息 */
    +  ogp: SeoContent,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +
    `,21),v=o(`

    比如你在使用某个第三方主题,并按照主题要求为每篇文章在 Front Matter 中设置了 banner,那你可以传入这样的 ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    同 OGP,你可以使用插件选项的 jsonLd 传入一个函数来按照你的需要修改默认 JSON-LD 对象并返回。

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

    规范链接

    如果你将内容部署到不同的站点,或不同 URL 下的相同内容,你可能需要设置 canonical 选项为你的页面提供 “规范链接”。 你可以设置一个字符串,这样它会附加在页面路由链接之前,或者添加一个自定义函数 (page: Page) => string | null 返回规范链接。

    例子

    如果你的站点部署在 example.com 的 docs 文件夹下,但同时在下列网址中可用:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (首选)

    要让搜索引擎结果始终是首选,你可能需要将 canonical 设置为 https://www.example.com/docs/,以便搜索引擎知道首选第四个 URL 作为索引结果。

    自定义 head 标签

    有些时候你可能需要符合其他协议或按照其他搜索引擎提供的格式提供对应的 SEO 标签,此时你可以使用 customHead 选项,其类型为:

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

    你应该直接修改传入的 head 参数。

    SEO 介绍

    搜索引擎优化 (Search Engine Optimization),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。 所谓“针对搜索引擎作最优化的处理”,是指为了要让网站更容易被搜索引擎接受。搜索引擎会将网站彼此间的内容做一些相关性的资料比对,然后再由浏览器将这些内容以最快速且接近最完整的方式,呈现给搜索者。搜索引擎优化就是通过搜索引擎的规则进行优化,为用户打造更好的用户体验,最终的目的就是做好用户体验。

    相关文档

    `,15),x={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},b=e("strong",null,"O",-1),C=e("strong",null,"G",-1),_=e("strong",null,"Pr",-1),E=e("p",null,[s("本插件完美支持该协议,会自动生成符合该协议的 "),e("code",null,""),s(" 标签。")],-1),f={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"本插件会为文章类页面生成 NewsArticle 类标签。",-1),w={href:"https://www.w3.org/TR/rdfa-primer/",target:"_blank",rel:"noopener noreferrer"},A={href:"https://schema.org/",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"结构标记的 Schema 定义站点",-1),S=e("h2",{id:"相关工具",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#相关工具"},[e("span",null,"相关工具")])],-1),O={href:"https://search.google.com/test/rich-results",target:"_blank",rel:"noopener noreferrer"};function B(F,L){const t=a("ExternalLinkIcon"),c=a("RouterLink"),d=a("ProjectLink");return r(),i("div",null,[g,e("p",null,[s("本插件会通过向网站 "),h,s(" 注入标签,让你的网站完全支持 "),e("a",y,[s("开放内容协议 OGP"),n(t)]),s(" 和 "),e("a",u,[s("JSON-LD 1.1"),n(t)]),s(",以全面增强站点的搜索引擎优化性。")]),m,e("p",null,[s("详细的参数结构详见 "),n(c,{to:"/zh/plugins/seo/config.html"},{default:l(()=>[s("配置")]),_:1}),s("。")]),v,e("ul",null,[e("li",null,[e("p",null,[e("a",x,[s("开放内容协议 OGP"),n(t)]),s(" ("),b,s("pen "),C,s("raph "),_,s("otocal)")]),E]),e("li",null,[e("p",null,[e("a",f,[s("JSON-LD 1.1"),n(t)])]),k]),e("li",null,[e("p",null,[e("a",w,[s("RDFa 1.1"),n(t)])]),e("p",null,[s("RDFa 主要标记 HTML 结构。这是插件无法支持的内容,"),n(d,{type:"theme",name:"hope",path:"/zh/"},{default:l(()=>[s("vuepress-theme-hope")]),_:1}),s(" 使用了这一功能通过了谷歌的富媒体结构测试。你可以考虑搭配使用。")])]),e("li",null,[e("p",null,[e("a",A,[s("Schema.Org"),n(t)])]),P])]),S,e("p",null,[s("你可以使用 "),e("a",O,[s("Google 富媒体结构测试工具"),n(t)]),s(" 测试本站点。")])])}const q=p(D,[["render",B],["__file","guide.html.vue"]]);export{q as default}; diff --git a/assets/guide.html-Jv9RswDw.js b/assets/guide.html-Jv9RswDw.js new file mode 100644 index 0000000000..d3edd3fa70 --- /dev/null +++ b/assets/guide.html-Jv9RswDw.js @@ -0,0 +1 @@ +import{_ as s,r as l,o as d,c,b as e,d as t,a as n,w as a,e as r}from"./app-GUhkEPRO.js";const h={},u=r('

    Guide

    Usage

    The plugin can generate feed files in the following three formats for you:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    Please set atom, json or rss to true in the plugin options according to the formats you want to generate.

    To correctly generate feed links, you need to set hostname in the plugin options,

    Readable Preview

    ',7),f={href:"/atom.xml",target:"_blank",rel:"noopener noreferrer"},p={href:"/rss.xml",target:"_blank",rel:"noopener noreferrer"},g=r('

    If you want to preview your feed in devServer, set devServer: true in plugin options. You may also need to set devHostname if you are not using the default http://localhost:{port}.

    Channel settings

    You can customize the feed channel information by setting the channel option.

    We recommend the following settings:

    • Convert the date of creating the feed to ISOString and write it into channel.pubDate
    • The update period of the content set in channel.ttl (unit: minutes)
    • Set copyright information via channel.copyright
    • Set the channel author via channel.author.
    ',5),_=e("h2",{id:"feed-generation",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#feed-generation"},[e("span",null,"Feed Generation")])],-1),m=e("p",null,"By default, all articles are added to the feed stream.",-1),v=e("code",null,"feed",-1),x=e("code",null,"getter",-1),b=e("h3",{id:"i18n-config",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#i18n-config"},[e("span",null,"I18n Config")])],-1),y=e("p",null,"The plugin generates separate feeds for each language.",-1),w=e("p",null,[t("You can provide different settings for different languages via "),e("code",null,"locales"),t(" in the plugin options.")],-1);function k(S,C){const i=l("ExternalLinkIcon"),o=l("RouterLink");return d(),c("div",null,[u,e("p",null,[t("When you open the feed file in browser, we magically convert atom and rss feed xml to human readable html via xsl template. Check "),e("a",f,[t("atom"),n(i)]),t(" and "),e("a",p,[t("rss"),n(i)]),t(" feed of this site as an example!")]),g,e("p",null,[t("For detailed options and their default values, see "),n(o,{to:"/plugins/feed/channel.html"},{default:a(()=>[t("Channel Config")]),_:1})]),_,m,e("p",null,[t("You can set "),v,t(" and other options in page frontmatter to control contents of feed item. See "),n(o,{to:"/plugins/feed/frontmatter.html"},{default:a(()=>[t("Frontmatter Config")]),_:1}),t(" for how they are converted.")]),e("p",null,[t("You can take full control of feed items generation by configuring the "),x,t(" in the plugin options. For detailed options and their default values, see "),n(o,{to:"/plugins/feed/getter.html"},{default:a(()=>[t("Configuration → Feed Getter")]),_:1}),t(".")]),b,y,w])}const I=s(h,[["render",k],["__file","guide.html.vue"]]);export{I as default}; diff --git a/assets/guide.html-MVe53ul9.js b/assets/guide.html-MVe53ul9.js new file mode 100644 index 0000000000..2e21838d73 --- /dev/null +++ b/assets/guide.html-MVe53ul9.js @@ -0,0 +1,37 @@ +import{_ as i,r as s,o as d,c as p,b as e,d as t,a,w as o,e as l}from"./app-GUhkEPRO.js";const g={},u=e("h1",{id:"guide",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#guide"},[e("span",null,"Guide")])],-1),h={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},m=l(`

    Out of Box

    The plugin works out of the box. Without any config, it will extract information from the page content as much as possible to complete the necessary tags required by OGP and JSON-LD.

    By default, the plugin will read the site config and page frontmatter to automatically generate tags as much as possible. Such as site name, page title, page type, writing date, last update date, and article tags are all automatically generated.

    The following are the <meta> tags and their values that will be injected into <head> by default:

    Default OGP Generation

    The following are the <meta> tags and their value injected into <head> by default to satisfy OGP:

    Meta NameValue
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || auto generated (when autoDescription is true in plugin options)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image ||first image in page || fallbackImage in plugin options
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternateOther languages in siteData.locales
    twitter:card"summary_large_image" (only available when image found)
    twitter:image:altpage.title (only available when image found)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    Default JSON-LD Generation

    Property NameValue
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    imageimage in page || options.hostname + page.frontmatter.image || siteFavIcon in plugin options
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    Setting Tags Directly

    You can configure the head option in the page's frontmatter to add specific tags to the page <head> to enhance SEO. For example:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    Will automatically inject <meta name="keywords" content="SEO plugin" />.

    Customize Generation

    The plugin also gives you full control over the build logic.

    Page Type

    For most pages, there are basically only two types: articles and website, so the plugin provides the isArticle option to allow you to provide logic for identifying articles.

    The option accepts a function in the format (page: Page) => boolean, by default all non-home pages generated from Markdown files are treated as articles.

    TIP

    If a page does fit into the "unpopular" genre like books, music, etc., you can handle them by setting the three options below.

    OGP

    You can use the plugin options ogp to pass in a function to modify the default OGP object to your needs and return it.

    function ogp(
    +  /** OGP Object inferred by plugin */
    +  ogp: SeoContent,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +
    `,22),D=l(`

    For example, if you are using a third-party theme and set a banner in frontmatter for each article according to the theme requirements, then you can pass in the following ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    Like OGP, you can use the plugin options jsonLd to pass in a function to modify the default JSON-LD object to your needs and return it.

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

    If you are deploying your content to different sites, or same content under different URLs, you may need to set canonical option to provide a "Canonical Link" for your page. You can either set a string which will be appended before page route link, or adding a custom function (page: Page) => string | null to return a canonical link if necessary.

    Example

    If your sites are deployed under docs directory in example.com, but available in:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (primary)

    To let search engine results always be the primary choice, you may need to set canonical to https://www.example.com/docs/, so that search engine will know that the fourth URL is preferred to be indexed.

    Customize head Tags

    Sometimes you may need to fit other protocols or provide the corresponding SEO tags in the format provided by other search engines. In this case, you can use the customHead option, whose type is:

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

    You should modify the head array in this function directly.

    SEO Introduction

    Search engine optimization (SEO) is the process of improving the quality and quantity of site traffic to a site or a web page from search engines. SEO targets unpaid traffic (known as "natural" or "organic" results) rather than direct traffic or paid traffic. Unpaid traffic may originate from different kinds of searches, including image search, video search, academic search, news search, and industry-specific vertical search engines.

    As an internet marketing strategy, SEO considers how search engines work, the computer-programmed algorithms that dictate search engine behavior, what people search for, the actual search terms or keywords typed into search engines, and which search engines are preferred by their targeted audience. SEO is performed because a site will receive more visitors from a search engine when sites rank higher on the search engine results page (SERP). These visitors can then potentially be converted into customers.

    `,16),f={href:"https://ogp.me/",target:"_blank",rel:"noopener noreferrer"},b=e("strong",null,"O",-1),v=e("strong",null,"G",-1),x=e("strong",null,"Pr",-1),C=e("p",null,[t("This plugin perfectly supports this protocol and will automatically generate "),e("code",null,""),t(" tags that conform to the protocol.")],-1),E={href:"https://www.w3.org/TR/json-ld-api/",target:"_blank",rel:"noopener noreferrer"},_=e("p",null,'This plugin will generate "NewsArticle" scheme for article pages.',-1),w={href:"https://www.w3.org/TR/rdfa-primer/",target:"_blank",rel:"noopener noreferrer"},k={href:"https://schema.org/",target:"_blank",rel:"noopener noreferrer"},O=e("p",null,"Schema definition site for structural markup",-1),P=e("h2",{id:"related-tools",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#related-tools"},[e("span",null,"Related Tools")])],-1),S={href:"https://search.google.com/test/rich-results",target:"_blank",rel:"noopener noreferrer"};function A(T,F){const n=s("ExternalLinkIcon"),r=s("RouterLink"),c=s("ProjectLink");return d(),p("div",null,[u,e("p",null,[t("This plugin will make your site fully support "),e("a",h,[t("Open Content Protocol OGP"),a(n)]),t(" and "),e("a",y,[t("JSON-LD 1.1"),a(n)]),t(" to enhance the SEO of the site.")]),m,e("p",null,[t("For detailed parameter structure, see "),a(r,{to:"/plugins/seo/config.html"},{default:o(()=>[t("Config")]),_:1}),t(".")]),D,e("ul",null,[e("li",null,[e("p",null,[e("a",f,[t("Open Content Protocol OGP"),a(n)]),t(" ("),b,t("pen "),v,t("raph "),x,t("otocol)")]),C]),e("li",null,[e("p",null,[e("a",E,[t("JSON-LD 1.1"),a(n)])]),_]),e("li",null,[e("p",null,[e("a",w,[t("RDFa 1.1"),a(n)])]),e("p",null,[t("RDFa mainly marks HTML structure. This is what the plugin cannot support. "),a(c,{type:"theme",name:"hope"},{default:o(()=>[t("vuepress-theme-hope")]),_:1}),t(" uses this feature to pass Google's rich media structure test. You can consider using it.")])]),e("li",null,[e("p",null,[e("a",k,[t("Schema.Org"),a(n)])]),O])]),P,e("p",null,[t("You can use "),e("a",S,[t("Google Rich Media Structure Test Tool"),a(n)]),t(" to test this site.")])])}const B=i(g,[["render",A],["__file","guide.html.vue"]]);export{B as default}; diff --git a/assets/guide.html-VwWU0xoR.js b/assets/guide.html-VwWU0xoR.js new file mode 100644 index 0000000000..75979fb26b --- /dev/null +++ b/assets/guide.html-VwWU0xoR.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2c2418e6","path":"/plugins/feed/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Readable Preview","slug":"readable-preview","link":"#readable-preview","children":[]},{"level":2,"title":"Channel settings","slug":"channel-settings","link":"#channel-settings","children":[]},{"level":2,"title":"Feed Generation","slug":"feed-generation","link":"#feed-generation","children":[{"level":3,"title":"I18n Config","slug":"i18n-config","link":"#i18n-config","children":[]}]}],"git":{"updatedTime":1706674769000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/feed/guide.md"}');export{e as data}; diff --git a/assets/guide.html-eW87z6FH.js b/assets/guide.html-eW87z6FH.js new file mode 100644 index 0000000000..a0e617212c --- /dev/null +++ b/assets/guide.html-eW87z6FH.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4c7e84d7","path":"/plugins/seo/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Out of Box","slug":"out-of-box","link":"#out-of-box","children":[{"level":3,"title":"Default OGP Generation","slug":"default-ogp-generation","link":"#default-ogp-generation","children":[]},{"level":3,"title":"Default JSON-LD Generation","slug":"default-json-ld-generation","link":"#default-json-ld-generation","children":[]}]},{"level":2,"title":"Setting Tags Directly","slug":"setting-tags-directly","link":"#setting-tags-directly","children":[]},{"level":2,"title":"Customize Generation","slug":"customize-generation","link":"#customize-generation","children":[{"level":3,"title":"Page Type","slug":"page-type","link":"#page-type","children":[]},{"level":3,"title":"OGP","slug":"ogp","link":"#ogp","children":[]},{"level":3,"title":"JSON-LD","slug":"json-ld","link":"#json-ld","children":[]}]},{"level":2,"title":"Canonical Link","slug":"canonical-link","link":"#canonical-link","children":[{"level":3,"title":"Customize head Tags","slug":"customize-head-tags","link":"#customize-head-tags","children":[]}]},{"level":2,"title":"SEO Introduction","slug":"seo-introduction","link":"#seo-introduction","children":[]},{"level":2,"title":"Related Documents","slug":"related-documents","link":"#related-documents","children":[]},{"level":2,"title":"Related Tools","slug":"related-tools","link":"#related-tools","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"plugins/seo/guide.md"}');export{e as data}; diff --git a/assets/guide.html-hXJzeUfu.js b/assets/guide.html-hXJzeUfu.js new file mode 100644 index 0000000000..9b27bc3c0a --- /dev/null +++ b/assets/guide.html-hXJzeUfu.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-678409c6","path":"/zh/plugins/seo/guide.html","title":"指南","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"开箱即用","slug":"开箱即用","link":"#开箱即用","children":[{"level":3,"title":"默认的 OGP 生成逻辑","slug":"默认的-ogp-生成逻辑","link":"#默认的-ogp-生成逻辑","children":[]},{"level":3,"title":"默认的 JSON-LD 生成逻辑","slug":"默认的-json-ld-生成逻辑","link":"#默认的-json-ld-生成逻辑","children":[]}]},{"level":2,"title":"直接添加 head 标签","slug":"直接添加-head-标签","link":"#直接添加-head-标签","children":[]},{"level":2,"title":"自定义生成过程","slug":"自定义生成过程","link":"#自定义生成过程","children":[{"level":3,"title":"页面类型","slug":"页面类型","link":"#页面类型","children":[]},{"level":3,"title":"OGP","slug":"ogp","link":"#ogp","children":[]},{"level":3,"title":"JSON-LD","slug":"json-ld","link":"#json-ld","children":[]}]},{"level":2,"title":"规范链接","slug":"规范链接","link":"#规范链接","children":[{"level":3,"title":"自定义 head 标签","slug":"自定义-head-标签","link":"#自定义-head-标签","children":[]}]},{"level":2,"title":"SEO 介绍","slug":"seo-介绍","link":"#seo-介绍","children":[]},{"level":2,"title":"相关文档","slug":"相关文档","link":"#相关文档","children":[]},{"level":2,"title":"相关工具","slug":"相关工具","link":"#相关工具","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":3}]},"filePathRelative":"zh/plugins/seo/guide.md"}');export{l as data}; diff --git a/assets/guide.html-mvLKR8iA.js b/assets/guide.html-mvLKR8iA.js new file mode 100644 index 0000000000..3836614da3 --- /dev/null +++ b/assets/guide.html-mvLKR8iA.js @@ -0,0 +1 @@ +import{_ as s,r as l,o as r,c as h,b as e,d as o,a as t,w as a,e as c}from"./app-GUhkEPRO.js";const i={},p=c('

    指南

    使用

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

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

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

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

    可读的预览

    ',7),_={href:"/zh/atom.xml",target:"_blank",rel:"noopener noreferrer"},u={href:"/zh/rss.xml",target:"_blank",rel:"noopener noreferrer"},f=c('

    如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true。如果你没有使用默认的 http://localhost:{port},你还需要设置 devHostname

    频道设置

    你可以通过设置 channel 选项来自自定义 Feed 频道的各项信息。

    我们推荐进行如下设置:

    • 将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
    • 通过 channel.ttl 中设置内容的更新周期(单位: 分钟)
    • 通过 channel.copyright 设置版权信息
    • 通过 channel.author 设置频道作者。
    ',5),m=e("h2",{id:"feed-生成",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#feed-生成"},[e("span",null,"Feed 生成")])],-1),x=e("p",null,"默认情况下,所有文章均会被添加至 feed 流。",-1),g=e("code",null,"feed",-1),F=e("code",null,"getter",-1),b=e("h3",{id:"多语言配置",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#多语言配置"},[e("span",null,"多语言配置")])],-1),k=e("p",null,"插件会针对每个语言生成单独的 Feed。",-1),S=e("p",null,[o("你可以通过插件选项中的 "),e("code",null,"locales"),o(" 分别对不同语言提供不同的默认设置。")],-1);function v(z,N){const d=l("ExternalLinkIcon"),n=l("RouterLink");return r(),h("div",null,[p,e("p",null,[o("当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 "),e("a",_,[o("atom"),t(d)]),o(" 和 "),e("a",u,[o("rss"),t(d)]),o(" feed 作为案例!")]),f,e("p",null,[o("详细的选项及其默认值详见 "),t(n,{to:"/zh/plugins/feed/channel.html"},{default:a(()=>[o("配置 → 频道设置")]),_:1})]),m,x,e("p",null,[o("你可以在 frontmatter 中配置 "),g,o(" 和其他选项控制每个页面的 Feed 项目内容,详见 "),t(n,{to:"/zh/plugins/feed/frontmatter.html"},{default:a(()=>[o("Frontmatter 选项")]),_:1}),o(" 了解它们如何被转换。")]),e("p",null,[o("你可以通过配置插件选项中的 "),F,o(" 完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 "),t(n,{to:"/zh/plugins/feed/getter.html"},{default:a(()=>[o("配置 → Feed 获取器")]),_:1})]),b,k,S])}const V=s(i,[["render",v],["__file","guide.html.vue"]]);export{V as default}; diff --git a/assets/guide.html-yQA_27h3.js b/assets/guide.html-yQA_27h3.js new file mode 100644 index 0000000000..570d8d6338 --- /dev/null +++ b/assets/guide.html-yQA_27h3.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-30402d02","path":"/plugins/sitemap/guide.html","title":"Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Control Sitemap Link","slug":"control-sitemap-link","link":"#control-sitemap-link","children":[]},{"level":2,"title":"Output Location","slug":"output-location","link":"#output-location","children":[]},{"level":2,"title":"Change Frequency","slug":"change-frequency","link":"#change-frequency","children":[]},{"level":2,"title":"Priority","slug":"priority","link":"#priority","children":[]},{"level":2,"title":"Modify Time","slug":"modify-time","link":"#modify-time","children":[]},{"level":2,"title":"Sitemap Intro","slug":"sitemap-intro","link":"#sitemap-intro","children":[]}],"git":{"updatedTime":1706723991000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/sitemap/guide.md"}');export{e as data}; diff --git a/assets/index.html-0H9Nmgxz.js b/assets/index.html-0H9Nmgxz.js new file mode 100644 index 0000000000..36a8dacb9f --- /dev/null +++ b/assets/index.html-0H9Nmgxz.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-873e9c0c","path":"/plugins/seo/","title":"seo","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/seo/README.md"}');export{e as data}; diff --git a/assets/index.html-0MkuVl9g.js b/assets/index.html-0MkuVl9g.js new file mode 100644 index 0000000000..0cb3949f95 --- /dev/null +++ b/assets/index.html-0MkuVl9g.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-8daa1a0e","path":"/","title":"Home","lang":"en-US","frontmatter":{"home":true,"title":"Home","heroImage":"/images/hero.png","footer":"MIT Licensed | Copyright © 2018-present VuePress Community"},"headers":[],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"README.md"}');export{e as data}; diff --git a/assets/index.html-293UxFOu.js b/assets/index.html-293UxFOu.js new file mode 100644 index 0000000000..5e63878f82 --- /dev/null +++ b/assets/index.html-293UxFOu.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-60e874ae","path":"/zh/plugins/seo/","title":"seo","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/seo/README.md"}');export{e as data}; diff --git a/assets/index.html-31AzVlYj.js b/assets/index.html-31AzVlYj.js new file mode 100644 index 0000000000..cb32fecc83 --- /dev/null +++ b/assets/index.html-31AzVlYj.js @@ -0,0 +1,9 @@ +import{_ as a,r as n,o as l,c as p,a as o,b as s,e as c}from"./app-GUhkEPRO.js";const t={},i=s("h1",{id:"使用方法",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用方法"},[s("span",null,"使用方法")])],-1),r=c(`

    安装默认主题:

    npm i -D @vuepress/theme-default@next
    +

    在配置文件中指定主题:

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    // 在这里添加主题配置
    +  }),
    +}
    +
    `,4);function d(u,m){const e=n("NpmBadge");return l(),p("div",null,[i,o(e,{package:"@vuepress/theme-default"}),r])}const D=a(t,[["render",d],["__file","index.html.vue"]]);export{D as default}; diff --git a/assets/index.html-5SZj0ulP.js b/assets/index.html-5SZj0ulP.js new file mode 100644 index 0000000000..be2ba5d34d --- /dev/null +++ b/assets/index.html-5SZj0ulP.js @@ -0,0 +1,11 @@ +import{_ as a,r as e,o as l,c as o,a as p,b as s,e as i}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"seo",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#seo"},[s("span",null,"seo")])],-1),t=i(`

    Usage

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,D){const n=e("NpmBadge");return l(),o("div",null,[r,p(n,{package:"@vuepress/plugin-seo"}),t])}const m=a(c,[["render",d],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-8D6ojdom.js b/assets/index.html-8D6ojdom.js new file mode 100644 index 0000000000..233065c995 --- /dev/null +++ b/assets/index.html-8D6ojdom.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-GUhkEPRO.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-BvtKXxHU.js b/assets/index.html-BvtKXxHU.js new file mode 100644 index 0000000000..438db5cbf9 --- /dev/null +++ b/assets/index.html-BvtKXxHU.js @@ -0,0 +1,11 @@ +import{_ as n,r as e,o as l,c as p,a as i,b as s,e as c}from"./app-GUhkEPRO.js";const o={},r=s("h1",{id:"sitemap",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sitemap"},[s("span",null,"sitemap")])],-1),t=c(`

    使用

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const a=e("NpmBadge");return l(),p("div",null,[r,i(a,{package:"@vuepress/plugin-sitemap"}),t])}const v=n(o,[["render",d],["__file","index.html.vue"]]);export{v as default}; diff --git a/assets/index.html-GUPgZrM2.js b/assets/index.html-GUPgZrM2.js new file mode 100644 index 0000000000..8f9b5102b0 --- /dev/null +++ b/assets/index.html-GUPgZrM2.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-37b048a2","path":"/plugins/sitemap/","title":"sitemap","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/sitemap/README.md"}');export{e as data}; diff --git a/assets/index.html-HTigedTr.js b/assets/index.html-HTigedTr.js new file mode 100644 index 0000000000..443e2fa7a3 --- /dev/null +++ b/assets/index.html-HTigedTr.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2e25e88b","path":"/zh/themes/default/","title":"使用方法","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/README.md"}');export{e as data}; diff --git a/assets/index.html-NRSJldfv.js b/assets/index.html-NRSJldfv.js new file mode 100644 index 0000000000..28204f0899 --- /dev/null +++ b/assets/index.html-NRSJldfv.js @@ -0,0 +1,11 @@ +import{_ as n,r as e,o as l,c as p,a as i,b as s,e as o}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"sitemap",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#sitemap"},[s("span",null,"sitemap")])],-1),t=o(`

    Usage

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,m){const a=e("NpmBadge");return l(),p("div",null,[r,i(a,{package:"@vuepress/plugin-sitemap"}),t])}const v=n(c,[["render",d],["__file","index.html.vue"]]);export{v as default}; diff --git a/assets/index.html-NqGL3aiX.js b/assets/index.html-NqGL3aiX.js new file mode 100644 index 0000000000..233065c995 --- /dev/null +++ b/assets/index.html-NqGL3aiX.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-GUhkEPRO.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-Ugn7-yHs.js b/assets/index.html-Ugn7-yHs.js new file mode 100644 index 0000000000..6530d5b942 --- /dev/null +++ b/assets/index.html-Ugn7-yHs.js @@ -0,0 +1,11 @@ +import{_ as a,r as e,o as l,c as p,a as o,b as s,e as i}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"seo",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#seo"},[s("span",null,"seo")])],-1),t=i(`

    使用

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function d(u,D){const n=e("NpmBadge");return l(),p("div",null,[r,o(n,{package:"@vuepress/plugin-seo"}),t])}const m=a(c,[["render",d],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-ZH7R9G3s.js b/assets/index.html-ZH7R9G3s.js new file mode 100644 index 0000000000..addc7cdd01 --- /dev/null +++ b/assets/index.html-ZH7R9G3s.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2d0ad528","path":"/zh/","title":"首页","lang":"zh-CN","frontmatter":{"home":true,"title":"首页","heroImage":"/images/hero.png","footer":"MIT 协议 | 版权所有 © 2018-至今 VuePress 社区"},"headers":[],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/README.md"}');export{e as data}; diff --git a/assets/index.html-aPphe-5B.js b/assets/index.html-aPphe-5B.js new file mode 100644 index 0000000000..1800c71ea0 --- /dev/null +++ b/assets/index.html-aPphe-5B.js @@ -0,0 +1,9 @@ +import{_ as a,r as n,o as l,c as t,a as o,b as s,e as p}from"./app-GUhkEPRO.js";const c={},i=s("h1",{id:"usage",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#usage"},[s("span",null,"Usage")])],-1),r=p(`

    Install @vuepress/theme-default :

    npm install @vuepress/theme-default@next
    +
    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    // set theme config here
    +  }),
    +}
    +
    `,3);function d(u,m){const e=n("NpmBadge");return l(),t("div",null,[i,o(e,{package:"@vuepress/theme-default"}),r])}const D=a(c,[["render",d],["__file","index.html.vue"]]);export{D as default}; diff --git a/assets/index.html-cmeFmgvT.js b/assets/index.html-cmeFmgvT.js new file mode 100644 index 0000000000..634362808e --- /dev/null +++ b/assets/index.html-cmeFmgvT.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-e78362cc","path":"/themes/default/","title":"Usage","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/README.md"}');export{e as data}; diff --git a/assets/index.html-dkUSQ5O2.js b/assets/index.html-dkUSQ5O2.js new file mode 100644 index 0000000000..f053c59c47 --- /dev/null +++ b/assets/index.html-dkUSQ5O2.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-21359efa","path":"/zh/plugins/feed/","title":"feed","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/feed/README.md"}');export{e as data}; diff --git a/assets/index.html-flT6MTap.js b/assets/index.html-flT6MTap.js new file mode 100644 index 0000000000..f6ec5fa58e --- /dev/null +++ b/assets/index.html-flT6MTap.js @@ -0,0 +1,11 @@ +import{_ as n,r as a,o as l,c as p,a as i,b as s,e as c}from"./app-GUhkEPRO.js";const o={},r=s("h1",{id:"feed",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#feed"},[s("span",null,"feed")])],-1),d=c(`

    使用

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    `,3);function t(u,D){const e=a("NpmBadge");return l(),p("div",null,[r,i(e,{package:"@vuepress/plugin-feed"}),d])}const m=n(o,[["render",t],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-g_k3EZSB.js b/assets/index.html-g_k3EZSB.js new file mode 100644 index 0000000000..39aa7aa27e --- /dev/null +++ b/assets/index.html-g_k3EZSB.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6203866e","path":"/plugins/feed/","title":"feed","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/feed/README.md"}');export{e as data}; diff --git a/assets/index.html-kAwxn97_.js b/assets/index.html-kAwxn97_.js new file mode 100644 index 0000000000..c39b000897 --- /dev/null +++ b/assets/index.html-kAwxn97_.js @@ -0,0 +1,11 @@ +import{_ as n,r as a,o as l,c as p,a as i,b as s,e as o}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"feed",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#feed"},[s("span",null,"feed")])],-1),d=o(`

    Usage

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    `,3);function t(u,D){const e=a("NpmBadge");return l(),p("div",null,[r,i(e,{package:"@vuepress/plugin-feed"}),d])}const m=n(c,[["render",t],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/index.html-nTgZ_LTS.js b/assets/index.html-nTgZ_LTS.js new file mode 100644 index 0000000000..aa8b862a36 --- /dev/null +++ b/assets/index.html-nTgZ_LTS.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5a88265e","path":"/zh/plugins/sitemap/","title":"sitemap","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[]}],"git":{"updatedTime":1706762763000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/sitemap/README.md"}');export{e as data}; diff --git a/assets/locale.html-_3zP9_4l.js b/assets/locale.html-_3zP9_4l.js new file mode 100644 index 0000000000..06606e0428 --- /dev/null +++ b/assets/locale.html-_3zP9_4l.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-d9440558","path":"/zh/themes/default/locale.html","title":"语言配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"repoLabel","slug":"repolabel","link":"#repolabel","children":[]},{"level":2,"title":"selectLanguageText","slug":"selectlanguagetext","link":"#selectlanguagetext","children":[]},{"level":2,"title":"selectLanguageAriaLabel","slug":"selectlanguagearialabel","link":"#selectlanguagearialabel","children":[]},{"level":2,"title":"selectLanguageName","slug":"selectlanguagename","link":"#selectlanguagename","children":[{"level":3,"title":"navbarLabel","slug":"navbarlabel","link":"#navbarlabel","children":[]},{"level":3,"title":"pageNavbarLabel","slug":"pagenavbarlabel","link":"#pagenavbarlabel","children":[]}]},{"level":2,"title":"editLinkText","slug":"editlinktext","link":"#editlinktext","children":[]},{"level":2,"title":"lastUpdatedText","slug":"lastupdatedtext","link":"#lastupdatedtext","children":[]},{"level":2,"title":"contributorsText","slug":"contributorstext","link":"#contributorstext","children":[]},{"level":2,"title":"tip","slug":"tip","link":"#tip","children":[]},{"level":2,"title":"warning","slug":"warning","link":"#warning","children":[]},{"level":2,"title":"danger","slug":"danger","link":"#danger","children":[]},{"level":2,"title":"notFound","slug":"notfound","link":"#notfound","children":[]},{"level":2,"title":"backToHome","slug":"backtohome","link":"#backtohome","children":[]},{"level":2,"title":"openInNewWindow","slug":"openinnewwindow","link":"#openinnewwindow","children":[]},{"level":2,"title":"toggleColorMode","slug":"togglecolormode","link":"#togglecolormode","children":[]},{"level":2,"title":"toggleSidebar","slug":"togglesidebar","link":"#togglesidebar","children":[]}],"git":{"updatedTime":1706734148000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/locale.md"}');export{e as data}; diff --git a/assets/locale.html-ayLbTzsN.js b/assets/locale.html-ayLbTzsN.js new file mode 100644 index 0000000000..410f6dba7f --- /dev/null +++ b/assets/locale.html-ayLbTzsN.js @@ -0,0 +1,21 @@ +import{_ as o,r as i,o as p,c,b as e,d as l,a as s,w as a,e as t}from"./app-GUhkEPRO.js";const d={},r=e("h1",{id:"locale-config",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#locale-config"},[e("span",null,"Locale Config")])],-1),u=e("p",null,"These options configure locale-related texts.",-1),h=e("p",null,"If your site is served in a different language besides English, you should set these options per locale to provide translations.",-1),f=e("h2",{id:"repolabel",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#repolabel"},[e("span",null,"repoLabel")])],-1),g=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),m=e("p",null,"Details:",-1),_=e("p",null,"Specify the repository label of your project.",-1),D=e("p",null,[l("This will be used as the text of the "),e("em",null,"repository link"),l(", which will be displayed as the last item of the navbar.")],-1),y=e("h2",{id:"selectlanguagetext",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#selectlanguagetext"},[e("span",null,"selectLanguageText")])],-1),b=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),v=e("p",null,"Details:",-1),x=e("p",null,[l("Specify the text of the "),e("em",null,"select language menu"),l(".")],-1),C=e("em",null,"select language menu",-1),T=t('

    selectLanguageAriaLabel

    • Type: string

    • Details:

      Specify the aria-label attribute of the select language menu.

      This is mainly for a11y purpose.

    selectLanguageName

    ',3),w=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),E=e("p",null,"Details:",-1),k=e("p",null,"Specify the name of the language of a locale.",-1),S=e("strong",null,"only take effect inside",-1),L=e("em",null,"select language menu",-1),N=e("li",null,[e("p",null,"Example:")],-1),F=t(`
    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      aria-label value for main navigation in navbar.

    • Type: null | string

    • Details:

      aria-label value for next/previous page navigation.

    editLinkText

    • Type: string

    • Default: 'Edit this page'

    • Details:

      Specify the text of the edit this page link.

    lastUpdatedText

    • Type: string

    • Default: 'Last Updated'

    • Details:

      Specify the text of the last updated timestamp label.

    contributorsText

    • Type: string

    • Default: 'Contributors'

    • Details:

      Specify the text of the contributors list label.

    tip

    `,12),I=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),A=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'TIP'")])],-1),B=e("p",null,"Details:",-1),R=e("h2",{id:"warning",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#warning"},[e("span",null,"warning")])],-1),V=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),z=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'WARNING'")])],-1),P=e("p",null,"Details:",-1),U=e("h2",{id:"danger",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#danger"},[e("span",null,"danger")])],-1),G=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),M=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'DANGER'")])],-1),W=e("p",null,"Details:",-1),j=t('

    notFound

    • Type: string[]

    • Default: ['Not Found']

    • Details:

      Specify the messages of the 404 page.

      The message will be randomly picked from the array when users enter the 404 page.

    backToHome

    • Type: string

    • Default: 'Back to home'

    • Details:

      Specify the text of the back to home link in the 404 page.

    openInNewWindow

    ',5),H=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),q=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'open in new window'")])],-1),J=e("p",null,"Details:",-1),K=e("code",null,"sr-only",-1),O=e("p",null,"This is mainly for a11y purpose.",-1),Q=e("p",null,"Also see:",-1),X=e("h2",{id:"togglecolormode",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#togglecolormode"},[e("span",null,"toggleColorMode")])],-1),Y=e("li",null,[e("p",null,[l("Type: "),e("code",null,"string")])],-1),Z=e("li",null,[e("p",null,[l("Default: "),e("code",null,"'toggle color mode'")])],-1),$=e("li",null,[e("p",null,"Details:"),e("p",null,"Title text for the color mode toggle button."),e("p",null,"This is mainly for a11y purpose.")],-1),ee=e("p",null,"Also see:",-1),le=t('

    toggleSidebar

    • Type: string

    • Default: 'toggle sidebar'

    • Details:

      Title text for sidebar toggle button.

      This is mainly for a11y purpose.

    ',2);function ne(se,ae){const n=i("RouterLink");return p(),c("div",null,[r,u,h,f,e("ul",null,[g,e("li",null,[m,_,D,e("p",null,[l("If you don't set this option explicitly, it will be automatically inferred from the "),s(n,{to:"/themes/default/config.html#repo"},{default:a(()=>[l("repo")]),_:1}),l(" option.")])])]),y,e("ul",null,[b,e("li",null,[v,x,e("p",null,[l("The "),C,l(" will appear next to the repository button in the navbar when you set multiple "),s(n,{to:"/themes/default/config.html#locales"},{default:a(()=>[l("locales")]),_:1}),l(" in your site config.")])])]),T,e("ul",null,[w,e("li",null,[E,k,e("p",null,[l("This option will "),S,l(" the "),s(n,{to:"/themes/default/config.html#locales"},{default:a(()=>[l("locales")]),_:1}),l(" of your theme config. It will be used as the language name of the locale, which will be displayed in the "),L,l(".")])]),N]),F,e("ul",null,[I,A,e("li",null,[B,e("p",null,[l("Specify the default title of the tip "),s(n,{to:"/themes/default/markdown.html#custom-containers"},{default:a(()=>[l("custom containers")]),_:1}),l(".")])])]),R,e("ul",null,[V,z,e("li",null,[P,e("p",null,[l("Specify the default title of the warning "),s(n,{to:"/themes/default/markdown.html#custom-containers"},{default:a(()=>[l("custom containers")]),_:1}),l(".")])])]),U,e("ul",null,[G,M,e("li",null,[W,e("p",null,[l("Specify the default title of the danger "),s(n,{to:"/themes/default/markdown.html#custom-containers"},{default:a(()=>[l("custom containers")]),_:1}),l(".")])])]),j,e("ul",null,[H,q,e("li",null,[J,e("p",null,[l("Specify the "),K,l(" text of the "),s(n,{to:"/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[l("ExternalLinkIcon")]),_:1}),l(".")]),O]),e("li",null,[Q,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/plugin.html#themeplugins-externallinkicon"},{default:a(()=>[l("Default Theme > Plugin Config > themePlugins.externalLinkIcon")]),_:1})])])])]),X,e("ul",null,[Y,Z,$,e("li",null,[ee,e("ul",null,[e("li",null,[s(n,{to:"/themes/default/config.html#colormodeswitch"},{default:a(()=>[l("Default Theme > Config > colorModeSwitch")]),_:1})])])])]),le])}const oe=o(d,[["render",ne],["__file","locale.html.vue"]]);export{oe as default}; diff --git a/assets/locale.html-ubQ9h3kZ.js b/assets/locale.html-ubQ9h3kZ.js new file mode 100644 index 0000000000..63ec792a1e --- /dev/null +++ b/assets/locale.html-ubQ9h3kZ.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-319287e5","path":"/themes/default/locale.html","title":"Locale Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"repoLabel","slug":"repolabel","link":"#repolabel","children":[]},{"level":2,"title":"selectLanguageText","slug":"selectlanguagetext","link":"#selectlanguagetext","children":[]},{"level":2,"title":"selectLanguageAriaLabel","slug":"selectlanguagearialabel","link":"#selectlanguagearialabel","children":[]},{"level":2,"title":"selectLanguageName","slug":"selectlanguagename","link":"#selectlanguagename","children":[{"level":3,"title":"navbarLabel","slug":"navbarlabel","link":"#navbarlabel","children":[]},{"level":3,"title":"pageNavbarLabel","slug":"pagenavbarlabel","link":"#pagenavbarlabel","children":[]}]},{"level":2,"title":"editLinkText","slug":"editlinktext","link":"#editlinktext","children":[]},{"level":2,"title":"lastUpdatedText","slug":"lastupdatedtext","link":"#lastupdatedtext","children":[]},{"level":2,"title":"contributorsText","slug":"contributorstext","link":"#contributorstext","children":[]},{"level":2,"title":"tip","slug":"tip","link":"#tip","children":[]},{"level":2,"title":"warning","slug":"warning","link":"#warning","children":[]},{"level":2,"title":"danger","slug":"danger","link":"#danger","children":[]},{"level":2,"title":"notFound","slug":"notfound","link":"#notfound","children":[]},{"level":2,"title":"backToHome","slug":"backtohome","link":"#backtohome","children":[]},{"level":2,"title":"openInNewWindow","slug":"openinnewwindow","link":"#openinnewwindow","children":[]},{"level":2,"title":"toggleColorMode","slug":"togglecolormode","link":"#togglecolormode","children":[]},{"level":2,"title":"toggleSidebar","slug":"togglesidebar","link":"#togglesidebar","children":[]}],"git":{"updatedTime":1706734148000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/locale.md"}');export{e as data}; diff --git a/assets/locale.html-umfeNPJn.js b/assets/locale.html-umfeNPJn.js new file mode 100644 index 0000000000..53e89326ff --- /dev/null +++ b/assets/locale.html-umfeNPJn.js @@ -0,0 +1,21 @@ +import{_ as p,r as t,o as c,c as d,b as l,d as n,a as e,w as a,e as o}from"./app-GUhkEPRO.js";const r={},u=l("h1",{id:"语言配置",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#语言配置"},[l("span",null,"语言配置")])],-1),h=l("p",null,"这些选项用于配置与语言相关的文本。",-1),_=l("p",null,"如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。",-1),g=l("h2",{id:"repolabel",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#repolabel"},[l("span",null,"repoLabel")])],-1),m=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),D=l("p",null,"详情:",-1),b=l("p",null,"项目仓库的标签。",-1),v=l("p",null,[n("它将被用作 "),l("em",null,"仓库链接"),n(" 的文字。"),l("em",null,"仓库链接"),n(" 将会显示为导航栏的最后一个元素。")],-1),f=l("h2",{id:"selectlanguagetext",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#selectlanguagetext"},[l("span",null,"selectLanguageText")])],-1),E=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),y=l("p",null,"详情:",-1),x=l("p",null,[l("em",null,"选择语言菜单"),n(" 的文字。")],-1),C={href:"https://v2.vuepress.vuejs.org/zh/config.html#locales",target:"_blank",rel:"noopener noreferrer"},k=l("em",null,"选择语言菜单",-1),A=o('

    selectLanguageAriaLabel

    • 类型: string

    • 详情:

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

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

    selectLanguageName

    ',3),w=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),L=l("p",null,"详情:",-1),N=l("p",null,"Locale 的语言名称。",-1),F=l("em",null,"选择语言菜单",-1),z=l("li",null,[l("p",null,"示例:")],-1),B=o(`
    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • 类型:null | string

    • 详情:

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

    • 类型:null | string

    • 详情:

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

    editLinkText

    • 类型: string

    • 默认值: 'Edit this page'

    • 详情:

      编辑此页 链接的文字。

    lastUpdatedText

    • 类型: string

    • 默认值: 'Last Updated'

    • 详情:

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

    contributorsText

    • 类型: string

    • 默认值: 'Contributors'

    • 详情:

      贡献者列表 标签的文字。

    tip

    `,12),T=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),I=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"'TIP'")])],-1),R=l("p",null,"详情:",-1),S=l("h2",{id:"warning",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#warning"},[l("span",null,"warning")])],-1),V=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),U=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"'WARNING'")])],-1),W=l("p",null,"详情:",-1),G=l("h2",{id:"danger",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#danger"},[l("span",null,"danger")])],-1),M=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),P=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"'DANGER'")])],-1),j=l("p",null,"详情:",-1),H=o('

    notFound

    • 类型: string[]

    • 默认值: ['Not Found']

    • 详情:

      404 页面的提示信息。

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

    backToHome

    • 类型: string

    • 默认值: 'Back to home'

    • 详情:

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

    openInNewWindow

    ',5),q=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),J=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"'open in new window'")])],-1),K=l("p",null,"详情:",-1),O=l("code",null,"sr-only",-1),Q=l("p",null,"它主要是为了站点的可访问性 (a11y) 。",-1),X=l("p",null,"参考:",-1),Y=l("h2",{id:"togglecolormode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#togglecolormode"},[l("span",null,"toggleColorMode")])],-1),Z=l("li",null,[l("p",null,[n("类型: "),l("code",null,"string")])],-1),$=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"'toggle color mode'")])],-1),ll=l("li",null,[l("p",null,"详情:"),l("p",null,"切换颜色模式按钮的标题文字。"),l("p",null,"它主要是为了站点的可访问性 (a11y) 。")],-1),nl=l("p",null,"参考:",-1),el=o('

    toggleSidebar

    • 类型: string

    • 默认值: 'toggle sidebar'

    • 详情:

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

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

    ',2);function sl(al,ol){const s=t("RouterLink"),i=t("ExternalLinkIcon");return c(),d("div",null,[u,h,_,g,l("ul",null,[m,l("li",null,[D,b,v,l("p",null,[n("如果你不明确指定该配置项,它将会根据 "),e(s,{to:"/zh/themes/default/config.html#repo"},{default:a(()=>[n("repo")]),_:1}),n(" 配置项自动推断。")])])]),f,l("ul",null,[E,l("li",null,[y,x,l("p",null,[n("如果你在站点配置中设置了多个 "),l("a",C,[n("locales"),e(i)]),n(" ,那么 "),k,n(" 就会显示在导航栏中仓库按钮的旁边。")])])]),A,l("ul",null,[w,l("li",null,[L,N,l("p",null,[n("该配置项 "),l("strong",null,[n("仅能在主题配置的 "),e(s,{to:"/zh/themes/default/config.html#locales"},{default:a(()=>[n("locales")]),_:1}),n(" 的内部生效")]),n(" 。它将被用作 locale 的语言名称,展示在 "),F,n(" 内。")])]),z]),B,l("ul",null,[T,I,l("li",null,[R,l("p",null,[n("Tip "),e(s,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:a(()=>[n("自定义容器")]),_:1}),n(" 的默认标题。")])])]),S,l("ul",null,[V,U,l("li",null,[W,l("p",null,[n("Warning "),e(s,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:a(()=>[n("自定义容器")]),_:1}),n(" 的默认标题。")])])]),G,l("ul",null,[M,P,l("li",null,[j,l("p",null,[n("Danger "),e(s,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:a(()=>[n("自定义容器")]),_:1}),n(" 的默认标题。")])])]),H,l("ul",null,[q,J,l("li",null,[K,l("p",null,[e(s,{to:"/zh/plugins/external-link-icon.html#externallinkicon"},{default:a(()=>[n("ExternalLinkIcon")]),_:1}),n(". 链接内的 "),O,n(" 文字。")]),Q]),l("li",null,[X,l("ul",null,[l("li",null,[e(s,{to:"/zh/themes/default/plugin.html#themeplugins-externallinkicon"},{default:a(()=>[n("默认主题 > 插件配置 > themePlugins.externalLinkIcon")]),_:1})])])])]),Y,l("ul",null,[Z,$,ll,l("li",null,[nl,l("ul",null,[l("li",null,[e(s,{to:"/zh/themes/default/config.html#colormodeswitch"},{default:a(()=>[n("默认主题 > 配置 > colorModeSwitch")]),_:1})])])])]),el])}const il=p(r,[["render",sl],["__file","locale.html.vue"]]);export{il as default}; diff --git a/assets/markdown.html-YIelTe7Z.js b/assets/markdown.html-YIelTe7Z.js new file mode 100644 index 0000000000..09cfdd7c76 --- /dev/null +++ b/assets/markdown.html-YIelTe7Z.js @@ -0,0 +1,50 @@ +import{_ as t,r as e,o as r,c as u,a as n,b as s,w as a,d as l,e as c}from"./app-GUhkEPRO.js";const D={},v=s("h1",{id:"markdown",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#markdown"},[s("span",null,"Markdown")])],-1),m=s("h2",{id:"自定义容器",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#自定义容器"},[s("span",null,"自定义容器")])],-1),b=c(`

    使用:

    ::: <type> [title]
    +[content]
    +:::
    +

    type 是必需的, titlecontent 是可选的。

    支持的 type 有:

    `,4),y=s("li",null,[s("code",null,"tip")],-1),g=s("li",null,[s("code",null,"warning")],-1),_=s("li",null,[s("code",null,"danger")],-1),h=s("li",null,[s("code",null,"details")],-1),k=s("ul",null,[s("li",null,[s("code",null,"code-group")]),s("li",null,[s("code",null,"code-group-item")])],-1),C=s("li",null,[s("p",null,"示例 1 (默认标题):")],-1),E=c(`

    输入

    ::: tip
    +这是一个提示
    +:::
    +
    +::: warning
    +这是一个警告
    +:::
    +
    +::: danger
    +这是一个危险警告
    +:::
    +
    +::: details
    +这是一个 details 标签
    +:::
    +

    输出

    提示

    这是一个提示

    注意

    这是一个警告

    警告

    这是一个危险警告

    这是一个 details 标签

    • 示例 2 (自定义标题):

    输入

    ::: danger STOP
    +危险区域,禁止通行
    +:::
    +
    +::: details 点击查看代码
    +
    +\`\`\`ts
    +console.log('你好,VuePress!')
    +\`\`\`
    +
    +:::
    +

    输出

    STOP

    危险区域,禁止通行

    点击查看代码
    console.log('你好,VuePress!')
    +
    • 示例 3 (Code Group 别名):

    输入

    :::: code-group
    +::: code-group-item FOO
    +
    +\`\`\`ts
    +const foo = 'foo'
    +\`\`\`
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +\`\`\`ts
    +const bar = 'bar'
    +\`\`\`
    +
    +:::
    +::::
    +

    输出

    `,17),f=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," foo"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'foo'")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1),x=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," bar"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'bar'")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1);function F(w,B){const p=e("NpmBadge"),i=e("RouterLink"),o=e("CodeGroupItem"),d=e("CodeGroup");return r(),u("div",null,[v,n(p,{package:"@vuepress/theme-default"}),m,s("ul",null,[s("li",null,[b,s("ul",null,[y,g,_,h,s("li",null,[n(i,{to:"/zh/themes/default/components.html#codegroup"},{default:a(()=>[l("CodeGroup")]),_:1}),l(" 和 "),n(i,{to:"/zh/themes/default/components.html#codegroupitem"},{default:a(()=>[l("CodeGroupItem")]),_:1}),l(" 的别名: "),k])])]),C]),E,n(d,null,{default:a(()=>[n(o,{title:"FOO"},{default:a(()=>[f]),_:1}),n(o,{title:"BAR"},{default:a(()=>[x]),_:1})]),_:1})])}const A=t(D,[["render",F],["__file","markdown.html.vue"]]);export{A as default}; diff --git a/assets/markdown.html-gZt7th1N.js b/assets/markdown.html-gZt7th1N.js new file mode 100644 index 0000000000..d68c180c60 --- /dev/null +++ b/assets/markdown.html-gZt7th1N.js @@ -0,0 +1,50 @@ +import{_ as d,r as e,o as r,c as u,a,b as s,d as n,w as l,e as c}from"./app-GUhkEPRO.js";const D={},m=s("h1",{id:"markdown",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#markdown"},[s("span",null,"Markdown")])],-1),v=s("h2",{id:"custom-containers",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#custom-containers"},[s("span",null,"Custom Containers")])],-1),b=c(`

    Usage:

    ::: <type> [title]
    +[content]
    +:::
    +

    The type is required, and the title and content are optional.

    Supported type :

    `,4),y=s("li",null,[s("code",null,"tip")],-1),g=s("li",null,[s("code",null,"warning")],-1),h=s("li",null,[s("code",null,"danger")],-1),_=s("li",null,[s("code",null,"details")],-1),k=s("ul",null,[s("li",null,[s("code",null,"code-group")]),s("li",null,[s("code",null,"code-group-item")])],-1),C=s("li",null,[s("p",null,"Example 1 (default title):")],-1),E=c(`

    Input

    ::: tip
    +This is a tip
    +:::
    +
    +::: warning
    +This is a warning
    +:::
    +
    +::: danger
    +This is a dangerous warning
    +:::
    +
    +::: details
    +This is a details block
    +:::
    +

    Output

    TIP

    This is a tip

    WARNING

    This is a warning

    DANGER

    This is a dangerous warning

    This is a details block

    • Example 2 (custom title):

    Input

    ::: danger STOP
    +Danger zone, do not proceed
    +:::
    +
    +::: details Click me to view the code
    +
    +\`\`\`ts
    +console.log('Hello, VuePress!')
    +\`\`\`
    +
    +:::
    +

    Output

    STOP

    Danger zone, do not proceed

    Click me to view the code
    console.log('Hello, VuePress!')
    +
    • Example 3 (code group alias):

    Input

    :::: code-group
    +::: code-group-item FOO
    +
    +\`\`\`ts
    +const foo = 'foo'
    +\`\`\`
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +\`\`\`ts
    +const bar = 'bar'
    +\`\`\`
    +
    +:::
    +::::
    +

    Output

    `,17),x=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," foo"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'foo'")]),n(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1),f=s("div",{class:"language-typescript line-numbers-mode","data-ext":"ts","data-title":"ts"},[s("pre",{class:"shiki dark-plus",style:{"background-color":"#1E1E1E",color:"#D4D4D4"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#569CD6"}},"const"),s("span",{style:{color:"#4FC1FF"}}," bar"),s("span",{style:{color:"#D4D4D4"}}," = "),s("span",{style:{color:"#CE9178"}},"'bar'")]),n(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"})])],-1);function w(F,T){const p=e("NpmBadge"),i=e("RouterLink"),o=e("CodeGroupItem"),t=e("CodeGroup");return r(),u("div",null,[m,a(p,{package:"@vuepress/theme-default"}),v,s("ul",null,[s("li",null,[b,s("ul",null,[y,g,h,_,s("li",null,[n("Alias of "),a(i,{to:"/themes/default/components.html#codegroup"},{default:l(()=>[n("CodeGroup")]),_:1}),n(" and "),a(i,{to:"/themes/default/components.html#codegroupitem"},{default:l(()=>[n("CodeGroupItem")]),_:1}),n(": "),k])])]),C]),E,a(t,null,{default:l(()=>[a(o,{title:"FOO"},{default:l(()=>[x]),_:1}),a(o,{title:"BAR"},{default:l(()=>[f]),_:1})]),_:1})])}const N=d(D,[["render",w],["__file","markdown.html.vue"]]);export{N as default}; diff --git a/assets/markdown.html-jmp42nVr.js b/assets/markdown.html-jmp42nVr.js new file mode 100644 index 0000000000..d28577128a --- /dev/null +++ b/assets/markdown.html-jmp42nVr.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-f8d6dac2","path":"/zh/themes/default/markdown.html","title":"Markdown","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"自定义容器","slug":"自定义容器","link":"#自定义容器","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/markdown.md"}');export{e as data}; diff --git a/assets/markdown.html-xk2DPo1W.js b/assets/markdown.html-xk2DPo1W.js new file mode 100644 index 0000000000..7601c4b5f6 --- /dev/null +++ b/assets/markdown.html-xk2DPo1W.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-66d0bcf0","path":"/themes/default/markdown.html","title":"Markdown","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Custom Containers","slug":"custom-containers","link":"#custom-containers","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/markdown.md"}');export{e as data}; diff --git a/assets/medium-zoom.html-7wkriUuL.js b/assets/medium-zoom.html-7wkriUuL.js new file mode 100644 index 0000000000..f7011eccac --- /dev/null +++ b/assets/medium-zoom.html-7wkriUuL.js @@ -0,0 +1,31 @@ +import{_ as i,r as o,o as c,c as r,a,b as s,d as n,e as l}from"./app-GUhkEPRO.js";const t={},d=s("h1",{id:"medium-zoom",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#medium-zoom"},[s("span",null,"medium-zoom")])],-1),m={href:"https://github.com/francoischalifour/medium-zoom#readme",target:"_blank",rel:"noopener noreferrer"},u=l(`

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

    使用方法

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

    配置项

    selector

    • 类型: string

    • 默认值: ':not(a) > img'

    • 详情:

      可缩放的图片的选择器。

      默认情况下,该插件会使 <a> 标签以外的所有图片都支持缩放。

    delay

    • 类型: number

    • 默认值: 500

    • 详情:

      以毫秒为单位的延迟。

      在切换路由进入一个新页面时,该插件会在一定延迟后才使页面内的图片支持缩放。

    zoomOptions

    `,10),D=s("li",null,[s("p",null,[n("类型: "),s("code",null,"Object")])],-1),h=s("li",null,[s("p",null,"详情:"),s("p",null,"medium-zoom 的配置项。")],-1),v=s("p",null,"参考:",-1),y={href:"https://github.com/francoischalifour/medium-zoom#options",target:"_blank",rel:"noopener noreferrer"},b=l(`

    样式

    你可以通过 zoomOptions 对大部分的缩放样式进行自定义,不过作为补充,该插件同样提供了一些 CSS 变量:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    `,5),C=s("p",null,"详情:",-1),_=s("code",null,"Zoom",-1),f={href:"https://github.com/francoischalifour/medium-zoom#methods",target:"_blank",rel:"noopener noreferrer"},g=s("p",null,"该插件会在切换路由进入当前页面时使图片支持缩放。但如果你要动态添加新图片,那么你可能就需要这个方法来让这些新图片也支持缩放。",-1),E=s("p",null,[n("该插件在 "),s("code",null,"Zoom"),n(" 实例上额外添加了一个 "),s("code",null,"refresh"),n(" 方法,它将使用 "),s("a",{href:"#selector"},"selector"),n(" 作为默认参数,先调用 "),s("code",null,"zoom.detach()"),n(" 再调用 "),s("code",null,"zoom.attach()"),n(" ,便于你快速刷新当前页面图片的缩放状态。")],-1),x=s("li",null,[s("p",null,"示例:")],-1),z=l(`
    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... 进行了一些操作,在当前页面添加了新的图片
    +
    +    // 此时你可能需要手动调用 \`refresh\` 来让这些新图片支持缩放
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    `,1);function k(A,F){const p=o("NpmBadge"),e=o("ExternalLinkIcon");return c(),r("div",null,[d,a(p,{package:"@vuepress/plugin-medium-zoom"}),s("p",null,[n("将 "),s("a",m,[n("medium-zoom"),a(e)]),n(" 集成到 VuePress 中,为图片提供可缩放的功能。")]),u,s("ul",null,[D,h,s("li",null,[v,s("ul",null,[s("li",null,[s("a",y,[n("medium-zoom > Options"),a(e)])])])])]),b,s("ul",null,[s("li",null,[C,s("p",null,[n("返回该插件使用的 "),_,n(" 实例,便于你直接使用实例上的 "),s("a",f,[n("methods"),a(e)]),n(" 。")]),g,E]),x]),z])}const Z=i(t,[["render",k],["__file","medium-zoom.html.vue"]]);export{Z as default}; diff --git a/assets/medium-zoom.html-axSoW9tP.js b/assets/medium-zoom.html-axSoW9tP.js new file mode 100644 index 0000000000..c16a06f173 --- /dev/null +++ b/assets/medium-zoom.html-axSoW9tP.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-658e4845","path":"/zh/plugins/medium-zoom.html","title":"medium-zoom","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"zoomOptions","slug":"zoomoptions","link":"#zoomoptions","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useMediumZoom","slug":"usemediumzoom","link":"#usemediumzoom","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/medium-zoom.md"}');export{e as data}; diff --git a/assets/medium-zoom.html-nUp5JSFy.js b/assets/medium-zoom.html-nUp5JSFy.js new file mode 100644 index 0000000000..eb46579d7f --- /dev/null +++ b/assets/medium-zoom.html-nUp5JSFy.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-145379d4","path":"/plugins/medium-zoom.html","title":"medium-zoom","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]},{"level":3,"title":"delay","slug":"delay","link":"#delay","children":[]},{"level":3,"title":"zoomOptions","slug":"zoomoptions","link":"#zoomoptions","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useMediumZoom","slug":"usemediumzoom","link":"#usemediumzoom","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/medium-zoom.md"}');export{e as data}; diff --git a/assets/medium-zoom.html-ztrN6D68.js b/assets/medium-zoom.html-ztrN6D68.js new file mode 100644 index 0000000000..631c311dda --- /dev/null +++ b/assets/medium-zoom.html-ztrN6D68.js @@ -0,0 +1,31 @@ +import{_ as p,r as o,o as t,c,a as e,b as s,d as n,e as l}from"./app-GUhkEPRO.js";const r={},d=s("h1",{id:"medium-zoom",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#medium-zoom"},[s("span",null,"medium-zoom")])],-1),m={href:"https://github.com/francoischalifour/medium-zoom#readme",target:"_blank",rel:"noopener noreferrer"},u=l(`

    This plugin has been integrated into the default theme.

    Usage

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

    Options

    selector

    • Type: string

    • Default: ':not(a) > img'

    • Details:

      Selector of zoomable images.

      By default this plugin will make all images zoomable except those inside <a> tags.

    delay

    • Type: number

    • Default: 500

    • Details:

      Delay in milliseconds.

      After navigating to a new page, this plugin will make images zoomable with a delay.

    zoomOptions

    `,10),D=s("li",null,[s("p",null,[n("Type: "),s("code",null,"Object")])],-1),h=s("li",null,[s("p",null,"Details:"),s("p",null,"Options for medium-zoom.")],-1),y=s("p",null,"Also see:",-1),v={href:"https://github.com/francoischalifour/medium-zoom#options",target:"_blank",rel:"noopener noreferrer"},b=l(`

    Styles

    You can customize most of the zoom styles via zoomOptions, while this plugin also provides some CSS variables for additional customization:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    `,5),g=s("p",null,"Details:",-1),C=s("code",null,"Zoom",-1),f={href:"https://github.com/francoischalifour/medium-zoom#methods",target:"_blank",rel:"noopener noreferrer"},_=s("p",null,"This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too.",-1),z=s("p",null,[n("This plugin adds an extra "),s("code",null,"refresh"),n(" method on the "),s("code",null,"Zoom"),n(" instance, which will call "),s("code",null,"zoom.detach()"),n(" then "),s("code",null,"zoom.attach()"),n(" with the "),s("a",{href:"#selector"},"selector"),n(" as the default parameter. It will help you to refresh the zoomable images for current page.")],-1),x=s("li",null,[s("p",null,"Example:")],-1),E=l(`
    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... do something to add new images in current page
    +
    +    // then you may need to call \`refresh\` manually to make those new images zoomable
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    `,1);function k(A,w){const i=o("NpmBadge"),a=o("ExternalLinkIcon");return t(),c("div",null,[d,e(i,{package:"@vuepress/plugin-medium-zoom"}),s("p",null,[n("Integrate "),s("a",m,[n("medium-zoom"),e(a)]),n(" into VuePress, which can provide the ability to zoom images.")]),u,s("ul",null,[D,h,s("li",null,[y,s("ul",null,[s("li",null,[s("a",v,[n("medium-zoom > Options"),e(a)])])])])]),b,s("ul",null,[s("li",null,[g,s("p",null,[n("Returns the "),C,n(" instance that used by this plugin, so that you can use the instance "),s("a",f,[n("methods"),e(a)]),n(" directly.")]),_,z]),x]),E])}const B=p(r,[["render",k],["__file","medium-zoom.html.vue"]]);export{B as default}; diff --git a/assets/nprogress.html-PAodSekb.js b/assets/nprogress.html-PAodSekb.js new file mode 100644 index 0000000000..5b6a6f259d --- /dev/null +++ b/assets/nprogress.html-PAodSekb.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-63215295","path":"/zh/plugins/nprogress.html","title":"nprogress","lang":"zh-CN","frontmatter":{"title":"nprogress"},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/nprogress.md"}');export{e as data}; diff --git a/assets/nprogress.html-QuAXox5S.js b/assets/nprogress.html-QuAXox5S.js new file mode 100644 index 0000000000..e9c625321d --- /dev/null +++ b/assets/nprogress.html-QuAXox5S.js @@ -0,0 +1,11 @@ +import{_ as o,r as a,o as p,c,a as e,b as s,d as n,e as i}from"./app-GUhkEPRO.js";const t={},d=s("h1",{id:"nprogress-插件",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#nprogress-插件"},[s("span",null,"nprogress 插件")])],-1),D={href:"https://github.com/rstacruz/nprogress",target:"_blank",rel:"noopener noreferrer"},u=i(`

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

    使用方法

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

    样式

    你可以通过 CSS 变量来自定义进度条的样式:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    `,7);function v(g,m){const l=a("NpmBadge"),r=a("ExternalLinkIcon");return p(),c("div",null,[d,e(l,{package:"@vuepress/plugin-nprogress"}),s("p",null,[n("将 "),s("a",D,[n("nprogress"),e(r)]),n(" 集成到 VuePress 中,在切换到另一个页面时会展示进度条。")]),u])}const y=o(t,[["render",v],["__file","nprogress.html.vue"]]);export{y as default}; diff --git a/assets/nprogress.html-Sat3hoSx.js b/assets/nprogress.html-Sat3hoSx.js new file mode 100644 index 0000000000..3e6b97b800 --- /dev/null +++ b/assets/nprogress.html-Sat3hoSx.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-481bcda6","path":"/plugins/nprogress.html","title":"nprogress","lang":"en-US","frontmatter":{"title":"nprogress"},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/nprogress.md"}');export{e as data}; diff --git a/assets/nprogress.html-cRlovdwN.js b/assets/nprogress.html-cRlovdwN.js new file mode 100644 index 0000000000..367e8fbb4e --- /dev/null +++ b/assets/nprogress.html-cRlovdwN.js @@ -0,0 +1,11 @@ +import{_ as o,r as e,o as p,c as i,a,b as s,d as n,e as t}from"./app-GUhkEPRO.js";const c={},d=s("h1",{id:"nprogress-plugin",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#nprogress-plugin"},[s("span",null,"nprogress Plugin")])],-1),u={href:"https://github.com/rstacruz/nprogress",target:"_blank",rel:"noopener noreferrer"},D=t(`

    This plugin has been integrated into the default theme.

    Usage

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

    Styles

    You can customize the style of the progress bar via CSS variables:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    `,7);function g(h,v){const l=e("NpmBadge"),r=e("ExternalLinkIcon");return p(),i("div",null,[d,a(l,{package:"@vuepress/plugin-nprogress"}),s("p",null,[n("Integrate "),s("a",u,[n("nprogress"),a(r)]),n(" into VuePress, which can provide a progress bar when navigating to another page.")]),D])}const y=o(c,[["render",g],["__file","nprogress.html.vue"]]);export{y as default}; diff --git a/assets/palette.html-EejcQ78z.js b/assets/palette.html-EejcQ78z.js new file mode 100644 index 0000000000..fd6480e6dc --- /dev/null +++ b/assets/palette.html-EejcQ78z.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-577d4e75","path":"/zh/plugins/palette.html","title":"palette","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"调色板和样式","slug":"调色板和样式","link":"#调色板和样式","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"使用调色板","slug":"使用调色板","link":"#使用调色板","children":[]},{"level":3,"title":"使用样式","slug":"使用样式","link":"#使用样式","children":[]}]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"preset","slug":"preset","link":"#preset","children":[]},{"level":3,"title":"userPaletteFile","slug":"userpalettefile","link":"#userpalettefile","children":[]},{"level":3,"title":"tempPaletteFile","slug":"temppalettefile","link":"#temppalettefile","children":[]},{"level":3,"title":"userStyleFile","slug":"userstylefile","link":"#userstylefile","children":[]},{"level":3,"title":"tempStyleFile","slug":"tempstylefile","link":"#tempstylefile","children":[]},{"level":3,"title":"importCode","slug":"importcode","link":"#importcode","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/palette.md"}');export{e as data}; diff --git a/assets/palette.html-IIBOm8e9.js b/assets/palette.html-IIBOm8e9.js new file mode 100644 index 0000000000..ad908e83c5 --- /dev/null +++ b/assets/palette.html-IIBOm8e9.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-304d59c6","path":"/plugins/palette.html","title":"palette","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Palette and Style","slug":"palette-and-style","link":"#palette-and-style","children":[]},{"level":2,"title":"Usage","slug":"usage-1","link":"#usage-1","children":[{"level":3,"title":"Usage of Palette","slug":"usage-of-palette","link":"#usage-of-palette","children":[]},{"level":3,"title":"Usage of Style","slug":"usage-of-style","link":"#usage-of-style","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"preset","slug":"preset","link":"#preset","children":[]},{"level":3,"title":"userPaletteFile","slug":"userpalettefile","link":"#userpalettefile","children":[]},{"level":3,"title":"tempPaletteFile","slug":"temppalettefile","link":"#temppalettefile","children":[]},{"level":3,"title":"userStyleFile","slug":"userstylefile","link":"#userstylefile","children":[]},{"level":3,"title":"tempStyleFile","slug":"tempstylefile","link":"#tempstylefile","children":[]},{"level":3,"title":"importCode","slug":"importcode","link":"#importcode","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/palette.md"}');export{e as data}; diff --git a/assets/palette.html-jilTbvVt.js b/assets/palette.html-jilTbvVt.js new file mode 100644 index 0000000000..2146fbba6b --- /dev/null +++ b/assets/palette.html-jilTbvVt.js @@ -0,0 +1,39 @@ +import{_ as i,r as n,o,c,a as l,b as e,d as s,e as p}from"./app-GUhkEPRO.js";const r={},d=e("h1",{id:"palette",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#palette"},[e("span",null,"palette")])],-1),u=p(`

    为你的主题提供调色板功能。

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

    对于主题作者,该插件可以帮助你提供用户自定义样式的能力。

    使用方法

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

    调色板和样式

    该插件会提供一个 @vuepress/plugin-palette/palette (调色板文件)和一个 @vuepress/plugin-palette/style (样式文件),用于在你的主题样式中引入。

    `,8),y={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties",target:"_blank",rel:"noopener noreferrer"},D={href:"https://sass-lang.com/documentation/variables",target:"_blank",rel:"noopener noreferrer"},v={href:"http://lesscss.org/features/#variables-feature",target:"_blank",rel:"noopener noreferrer"},h={href:"https://stylus-lang.com/docs/variables.html",target:"_blank",rel:"noopener noreferrer"},m=p(`

    样式文件用于覆盖默认样式或添加额外样式,因此它一般会在你主题样式的末尾引入。

    使用

    在你的主题中使用该插件,假设你使用 SASS 作为 CSS 预处理器:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    使用调色板

    在你主题需要使用对应变量的地方引入该插件的调色板文件,比如在 Layout.vue 中:

    <template>
    +  <h1 class="palette-title">你好,调色板!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* 从该插件的调色板中引入变量 */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* 设置变量的默认值 */
    +$color: red !default;
    +
    +/* 在你的样式中使用变量 */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    然后,用户就可以在 .vuepress/styles/palette.scss 中自定义变量:

    $color: green;
    +

    使用样式

    在你主题的样式之后引入该插件的样式文件,比如在 clientConfigFile 中:

    // 引入你主题本身的样式文件
    +import 'path/to/your/theme/style'
    +// 引入该插件的样式文件
    +import '@vuepress/plugin-palette/style'
    +

    然后,用户就可以在 .vuepress/styles/index.scss 中添加额外样式,并可以覆盖你主题本身的样式:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    配置项

    preset

    • 类型: 'css' | 'sass' | 'less' | 'stylus'

    • 默认值: 'css'

    • 详情:

      设置其他选项的预设。

      如果你没有对该插件进行进阶定制化的需要,建议只设置该配置项并忽略其他选项。

    userPaletteFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • 详情:

      用户调色板文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户定义样式变量,建议保持默认值作为约定的文件路径。

    tempPaletteFile

    • 类型: string

    • 默认值:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • 详情:

      生成的调色板临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/palette' 别名来引入调色板文件,因此在绝大多数情况下你不需要修改该配置项。

    userStyleFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • 详情:

      用户样式文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户覆盖默认样式和添加额外样式,建议保持默认值作为约定的文件路径。

    tempStyleFile

    • 类型: string

    • 默认值:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • 详情:

      生成的样式临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/style' 别名来引入样式文件,因此在绝大多数情况下你不需要修改该配置项。

    importCode

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

    • 默认值:

      • css: (filePath) => \`@import '\${filePath}';\\n\`
      • sass: (filePath) => \`@forward 'file:///\${filePath}';\\n\`
      • less: (filePath) => \`@import '\${filePath}';\\n\`
      • stylus: (filePath) => \`@require '\${filePath}';\\n\`
    • 详情:

      用于生成引入代码的函数。

      默认值依赖于 preset 配置项。

      该配置项用于生成 tempPaletteFiletempStyleFile ,在绝大多数情况下你不需要修改该配置项。

    `,27);function b(C,g){const t=n("NpmBadge"),a=n("ExternalLinkIcon");return o(),c("div",null,[d,l(t,{package:"@vuepress/plugin-palette"}),u,e("p",null,[s("调色板文件用于定义样式变量,因此它一般会在你主题样式的开头引入。举例来说,用户可以在调色板中定义 "),e("a",y,[s("CSS 变量"),l(a)]),s(" 、 "),e("a",D,[s("SASS 变量"),l(a)]),s(" 、 "),e("a",v,[s("LESS 变量"),l(a)]),s(" 或 "),e("a",h,[s("Stylus 变量"),l(a)]),s(" ,然后你可以在你的主题样式中使用这些变量。")]),m])}const E=i(r,[["render",b],["__file","palette.html.vue"]]);export{E as default}; diff --git a/assets/palette.html-rQPFw1I-.js b/assets/palette.html-rQPFw1I-.js new file mode 100644 index 0000000000..f9290f5945 --- /dev/null +++ b/assets/palette.html-rQPFw1I-.js @@ -0,0 +1,39 @@ +import{_ as i,r as n,o,c as r,a as l,b as s,d as e,e as t}from"./app-GUhkEPRO.js";const c={},d=s("h1",{id:"palette",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#palette"},[s("span",null,"palette")])],-1),u=t(`

    Provide palette support for your theme.

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

    For theme authors, this plugin will help you to provide styles customization for users.

    Usage

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

    Palette and Style

    This plugin will provide a @vuepress/plugin-palette/palette (palette file) and a @vuepress/plugin-palette/style (style file) to be imported in your theme styles.

    `,8),y={href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties",target:"_blank",rel:"noopener noreferrer"},h={href:"https://sass-lang.com/documentation/variables",target:"_blank",rel:"noopener noreferrer"},v={href:"http://lesscss.org/features/#variables-feature",target:"_blank",rel:"noopener noreferrer"},D={href:"https://stylus-lang.com/docs/variables.html",target:"_blank",rel:"noopener noreferrer"},m=t(`

    The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles.

    Usage

    Use this plugin in your theme, assuming you are using SASS:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    Usage of Palette

    Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the Layout.vue file:

    <template>
    +  <h1 class="palette-title">Hello, Palette!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* import variables from the plugin's palette file */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* set default value for variables */
    +$color: red !default;
    +
    +/* use variables in your styles */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    Then users can customize variables in .vuepress/styles/palette.scss:

    $color: green;
    +

    Usage of Style

    Import the plugin's style file after your theme's styles, for example, in the clientConfigFile:

    // import your theme's style file
    +import 'path/to/your/theme/style'
    +// import the plugin's style file
    +import '@vuepress/plugin-palette/style'
    +

    Then users can add extra styles in .vuepress/styles/index.scss and override the default styles of your theme:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    Options

    preset

    • Type: 'css' | 'sass' | 'less' | 'stylus'

    • Default: 'css'

    • Details:

      Set preset for other options.

      If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options.

    userPaletteFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • Details:

      File path of the user palette file, relative to source directory.

      The default value depends on the preset option.

      The file is where users define style variables, and it's recommended to keep the default file path as a convention.

    tempPaletteFile

    • Type: string

    • Default:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • Details:

      File path of the generated palette temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the palette file via '@vuepress/plugin-palette/palette' alias, so you don't need to change this option in most cases.

    userStyleFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • Details:

      File path of the user style file, relative to source directory.

      The default value depends on the preset option.

      The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention.

    tempStyleFile

    • Type: string

    • Default:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • Details:

      File path of the generated style temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the style file via '@vuepress/plugin-palette/style' alias, so you don't need to change this option in most cases.

    importCode

    • Type: (filePath: string) => string

    • Default:

      • css: (filePath) => \`@import '\${filePath}';\\n\`
      • sass: (filePath) => \`@forward 'file:///\${filePath}';\\n\`
      • less: (filePath) => \`@import '\${filePath}';\\n\`
      • stylus: (filePath) => \`@require '\${filePath}';\\n\`
    • Details:

      Function to generate import code.

      The default value depends on the preset option.

      This option is used for generating tempPaletteFile and tempStyleFile, and you don't need to change this option in most cases.

    `,27);function f(b,g){const p=n("NpmBadge"),a=n("ExternalLinkIcon");return o(),r("div",null,[d,l(p,{package:"@vuepress/plugin-palette"}),u,s("p",null,[e("The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define "),s("a",y,[e("CSS variables"),l(a)]),e(", "),s("a",h,[e("SASS variables"),l(a)]),e(", "),s("a",v,[e("LESS variables"),l(a)]),e(" or "),s("a",D,[e("Stylus variables"),l(a)]),e(" in the palette, and then you can use those variables in your theme styles.")]),m])}const x=i(c,[["render",f],["__file","palette.html.vue"]]);export{x as default}; diff --git a/assets/plugin.html-CQ1xhZaI.js b/assets/plugin.html-CQ1xhZaI.js new file mode 100644 index 0000000000..7f92078adc --- /dev/null +++ b/assets/plugin.html-CQ1xhZaI.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-15ee72a8","path":"/themes/default/plugin.html","title":"Plugins Config","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"themePlugins.activeHeaderLinks","slug":"themeplugins-activeheaderlinks","link":"#themeplugins-activeheaderlinks","children":[]},{"level":2,"title":"themePlugins.backToTop","slug":"themeplugins-backtotop","link":"#themeplugins-backtotop","children":[]},{"level":2,"title":"themePlugins.container","slug":"themeplugins-container","link":"#themeplugins-container","children":[]},{"level":2,"title":"themePlugins.externalLinkIcon","slug":"themeplugins-externallinkicon","link":"#themeplugins-externallinkicon","children":[]},{"level":2,"title":"themePlugins.copyCode","slug":"themeplugins-copycode","link":"#themeplugins-copycode","children":[]},{"level":2,"title":"themePlugins.git","slug":"themeplugins-git","link":"#themeplugins-git","children":[]},{"level":2,"title":"themePlugins.mediumZoom","slug":"themeplugins-mediumzoom","link":"#themeplugins-mediumzoom","children":[]},{"level":2,"title":"themePlugins.nprogress","slug":"themeplugins-nprogress","link":"#themeplugins-nprogress","children":[]},{"level":2,"title":"themePlugins.seo","slug":"themeplugins-seo","link":"#themeplugins-seo","children":[]},{"level":2,"title":"themePlugins.sitemap","slug":"themeplugins-sitemap","link":"#themeplugins-sitemap","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"themes/default/plugin.md"}');export{e as data}; diff --git a/assets/plugin.html-o0jzXsiQ.js b/assets/plugin.html-o0jzXsiQ.js new file mode 100644 index 0000000000..2a4137629e --- /dev/null +++ b/assets/plugin.html-o0jzXsiQ.js @@ -0,0 +1,10 @@ +import{_ as i,r as a,o as u,c as p,b as l,d as e,a as s,w as o,e as t}from"./app-GUhkEPRO.js";const c={},d=t(`

    Plugins Config

    You can configure the plugins that used by default theme with themePlugins.

    Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it.

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // customize theme plugins here
    +    },
    +  }),
    +}
    +
    `,5),r=l("li",null,[l("p",null,[e("Type: "),l("code",null,"boolean")])],-1),h=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),_=l("p",null,"Details:",-1),m=l("h2",{id:"themeplugins-backtotop",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-backtotop"},[l("span",null,"themePlugins.backToTop")])],-1),g=l("li",null,[l("p",null,[e("Type: "),l("code",null,"BackToTopPluginOptions | boolean")])],-1),b=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),f=l("p",null,"Details:",-1),D=l("p",null,"Object value is supported as plugin options.",-1),v=l("h2",{id:"themeplugins-container",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-container"},[l("span",null,"themePlugins.container")])],-1),y=l("li",null,[l("p",null,[e("Type: "),l("code",null,"Record")])],-1),C=l("p",null,"Details:",-1),k=t("

    ContainerType type is:

    • tip
    • warning
    • danger
    • details
    • codeGroup
    • codeGroupItem
    ",2),x=l("p",null,"Also see:",-1),T=l("h2",{id:"themeplugins-externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-externallinkicon"},[l("span",null,"themePlugins.externalLinkIcon")])],-1),E=l("li",null,[l("p",null,[e("Type: "),l("code",null,"boolean")])],-1),P=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),w=l("p",null,"Details:",-1),O=l("h2",{id:"themeplugins-copycode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-copycode"},[l("span",null,"themePlugins.copyCode")])],-1),z=l("li",null,[l("p",null,[e("Type: "),l("code",null,"CopyCodePluginOptions | boolean")])],-1),j=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),A=l("p",null,"Details:",-1),B=l("p",null,"Object value is supported as plugin options.",-1),L=l("h2",{id:"themeplugins-git",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-git"},[l("span",null,"themePlugins.git")])],-1),N=l("li",null,[l("p",null,[e("Type: "),l("code",null,"boolean")])],-1),V=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),F=l("p",null,"Details:",-1),R=l("h2",{id:"themeplugins-mediumzoom",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-mediumzoom"},[l("span",null,"themePlugins.mediumZoom")])],-1),S=l("li",null,[l("p",null,[e("Type: "),l("code",null,"boolean")])],-1),G=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),I=l("p",null,"Details:",-1),M=l("h2",{id:"themeplugins-nprogress",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-nprogress"},[l("span",null,"themePlugins.nprogress")])],-1),Y=l("li",null,[l("p",null,[e("Type: "),l("code",null,"boolean")])],-1),H=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),Z=l("p",null,"Details:",-1),q=l("h2",{id:"themeplugins-seo",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-seo"},[l("span",null,"themePlugins.seo")])],-1),J=l("li",null,[l("p",null,[e("Type: "),l("code",null,"SeoPluginOptions | boolean")])],-1),K=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),Q=l("p",null,"Details:",-1),U=l("p",null,"Object value is supported as plugin options.",-1),W=l("h2",{id:"themeplugins-sitemap",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-sitemap"},[l("span",null,"themePlugins.sitemap")])],-1),X=l("li",null,[l("p",null,[e("Type: "),l("code",null,"SitemapPluginOptions | boolean")])],-1),$=l("li",null,[l("p",null,[e("Default: "),l("code",null,"true")])],-1),ll=l("p",null,"Details:",-1),el=l("p",null,"Object value is supported as plugin options.",-1);function nl(sl,ol){const n=a("RouterLink");return u(),p("div",null,[d,l("ul",null,[r,h,l("li",null,[_,l("p",null,[e("Enable "),s(n,{to:"/plugins/active-header-links.html"},{default:o(()=>[e("@vuepress/plugin-active-header-links")]),_:1}),e(" or not.")])])]),m,l("ul",null,[g,b,l("li",null,[f,l("p",null,[e("Enable "),s(n,{to:"/plugins/back-to-top.html"},{default:o(()=>[e("@vuepress/plugin-back-to-top")]),_:1}),e(" or not.")]),D])]),v,l("ul",null,[y,l("li",null,[C,l("p",null,[e("Enable custom containers that powered by "),s(n,{to:"/plugins/container.html"},{default:o(()=>[e("@vuepress/plugin-container")]),_:1}),e(" or not.")]),k]),l("li",null,[x,l("ul",null,[l("li",null,[s(n,{to:"/themes/default/markdown.html#custom-containers"},{default:o(()=>[e("Default Theme > Markdown > Custom Containers")]),_:1})])])])]),T,l("ul",null,[E,P,l("li",null,[w,l("p",null,[e("Enable "),s(n,{to:"/plugins/external-link-icon.html"},{default:o(()=>[e("@vuepress/plugin-external-link-icon")]),_:1}),e(" or not.")])])]),O,l("ul",null,[z,j,l("li",null,[A,l("p",null,[e("Enable "),s(n,{to:"/plugins/copy-code.html"},{default:o(()=>[e("@vuepress/plugin-copy-code")]),_:1}),e(" or not.")]),B])]),L,l("ul",null,[N,V,l("li",null,[F,l("p",null,[e("Enable "),s(n,{to:"/plugins/git.html"},{default:o(()=>[e("@vuepress/plugin-git")]),_:1}),e(" or not.")])])]),R,l("ul",null,[S,G,l("li",null,[I,l("p",null,[e("Enable "),s(n,{to:"/plugins/medium-zoom.html"},{default:o(()=>[e("@vuepress/plugin-medium-zoom")]),_:1}),e(" or not.")])])]),M,l("ul",null,[Y,H,l("li",null,[Z,l("p",null,[e("Enable "),s(n,{to:"/plugins/nprogress.html"},{default:o(()=>[e("@vuepress/plugin-nprogress")]),_:1}),e(" or not.")])])]),q,l("ul",null,[J,K,l("li",null,[Q,l("p",null,[e("Enable "),s(n,{to:"/plugins/seo/"},{default:o(()=>[e("@vuepress/plugin-seo")]),_:1}),e(" or not.")]),U])]),W,l("ul",null,[X,$,l("li",null,[ll,l("p",null,[e("Enable "),s(n,{to:"/plugins/sitemap/"},{default:o(()=>[e("@vuepress/plugin-sitemap")]),_:1}),e(" or not.")]),el])])])}const il=i(c,[["render",nl],["__file","plugin.html.vue"]]);export{il as default}; diff --git a/assets/plugin.html-o6xbQm-S.js b/assets/plugin.html-o6xbQm-S.js new file mode 100644 index 0000000000..59280dba2e --- /dev/null +++ b/assets/plugin.html-o6xbQm-S.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-56d43c1b","path":"/zh/themes/default/plugin.html","title":"插件配置","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"themePlugins.activeHeaderLinks","slug":"themeplugins-activeheaderlinks","link":"#themeplugins-activeheaderlinks","children":[{"level":3,"title":"themePlugins.backToTop","slug":"themeplugins-backtotop","link":"#themeplugins-backtotop","children":[]}]},{"level":2,"title":"themePlugins.container","slug":"themeplugins-container","link":"#themeplugins-container","children":[]},{"level":2,"title":"themePlugins.copyCode","slug":"themeplugins-copycode","link":"#themeplugins-copycode","children":[]},{"level":2,"title":"themePlugins.externalLinkIcon","slug":"themeplugins-externallinkicon","link":"#themeplugins-externallinkicon","children":[]},{"level":2,"title":"themePlugins.git","slug":"themeplugins-git","link":"#themeplugins-git","children":[]},{"level":2,"title":"themePlugins.mediumZoom","slug":"themeplugins-mediumzoom","link":"#themeplugins-mediumzoom","children":[]},{"level":2,"title":"themePlugins.nprogress","slug":"themeplugins-nprogress","link":"#themeplugins-nprogress","children":[]},{"level":2,"title":"themePlugins.seo","slug":"themeplugins-seo","link":"#themeplugins-seo","children":[]},{"level":2,"title":"themePlugins.sitemap","slug":"themeplugins-sitemap","link":"#themeplugins-sitemap","children":[]}],"git":{"updatedTime":1706960012000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/themes/default/plugin.md"}');export{e as data}; diff --git a/assets/plugin.html-w_pj_Syc.js b/assets/plugin.html-w_pj_Syc.js new file mode 100644 index 0000000000..81b9536c09 --- /dev/null +++ b/assets/plugin.html-w_pj_Syc.js @@ -0,0 +1,10 @@ +import{_ as i,r as u,o as a,c as p,b as l,d as n,a as s,w as o,e as t}from"./app-GUhkEPRO.js";const c={},d=t(`

    插件配置

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

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

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // 在这里自定义主题插件
    +    },
    +  }),
    +}
    +
    `,5),h=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),r=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),_=l("p",null,"详情:",-1),m=l("h3",{id:"themeplugins-backtotop",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-backtotop"},[l("span",null,"themePlugins.backToTop")])],-1),g=l("li",null,[l("p",null,[n("类型: "),l("code",null,"BackToTopPluginOptions | boolean")])],-1),v=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),b=l("p",null,"详情:",-1),f=l("p",null,"支持对象格式以作为插件选项。",-1),D=l("h2",{id:"themeplugins-container",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-container"},[l("span",null,"themePlugins.container")])],-1),y=l("li",null,[l("p",null,[n("类型: "),l("code",null,"Record")])],-1),k=l("p",null,"详情:",-1),x=t("

    ContainerType 类型为:

    • tip
    • warning
    • danger
    • details
    • codeGroup
    • codeGroupItem
    ",2),C=l("p",null,"参考:",-1),P=l("h2",{id:"themeplugins-copycode",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-copycode"},[l("span",null,"themePlugins.copyCode")])],-1),z=l("li",null,[l("p",null,[n("类型: "),l("code",null,"CopyCodePluginOptions | boolean")])],-1),E=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),A=l("p",null,"详情:",-1),T=l("p",null,"支持对象格式以作为插件选项。",-1),B=l("h2",{id:"themeplugins-externallinkicon",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-externallinkicon"},[l("span",null,"themePlugins.externalLinkIcon")])],-1),w=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),L=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),N=l("p",null,"详情:",-1),O=l("h2",{id:"themeplugins-git",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-git"},[l("span",null,"themePlugins.git")])],-1),V=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),F=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),R=l("p",null,"详情:",-1),S=l("h2",{id:"themeplugins-mediumzoom",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-mediumzoom"},[l("span",null,"themePlugins.mediumZoom")])],-1),G=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),I=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),H=l("p",null,"详情:",-1),M=l("h2",{id:"themeplugins-nprogress",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-nprogress"},[l("span",null,"themePlugins.nprogress")])],-1),Z=l("li",null,[l("p",null,[n("类型: "),l("code",null,"boolean")])],-1),j=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),q=l("p",null,"详情:",-1),J=l("h2",{id:"themeplugins-seo",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-seo"},[l("span",null,"themePlugins.seo")])],-1),K=l("li",null,[l("p",null,[n("类型: "),l("code",null,"SeoPluginOptions | boolean")])],-1),Q=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),U=l("p",null,"详情:",-1),W=l("p",null,"支持对象格式以作为插件选项。",-1),X=l("h2",{id:"themeplugins-sitemap",tabindex:"-1"},[l("a",{class:"header-anchor",href:"#themeplugins-sitemap"},[l("span",null,"themePlugins.sitemap")])],-1),Y=l("li",null,[l("p",null,[n("类型: "),l("code",null,"SitemapPluginOptions | boolean")])],-1),$=l("li",null,[l("p",null,[n("默认值: "),l("code",null,"true")])],-1),ll=l("p",null,"详情:",-1),nl=l("p",null,"支持对象格式以作为插件选项。",-1);function el(sl,ol){const e=u("RouterLink");return a(),p("div",null,[d,l("ul",null,[h,r,l("li",null,[_,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/active-header-links.html"},{default:o(()=>[n("@vuepress/plugin-active-header-links")]),_:1}),n(" 。")])])]),m,l("ul",null,[g,v,l("li",null,[b,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/back-to-top.html"},{default:o(()=>[n("@vuepress/plugin-back-to-top")]),_:1}),n(" 。")]),f])]),D,l("ul",null,[y,l("li",null,[k,l("p",null,[n("是否启用由 "),s(e,{to:"/zh/plugins/container.html"},{default:o(()=>[n("@vuepress/plugin-container")]),_:1}),n(" 支持的自定义容器。")]),x]),l("li",null,[C,l("ul",null,[l("li",null,[s(e,{to:"/zh/themes/default/markdown.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AE%B9%E5%99%A8"},{default:o(()=>[n("默认主题 > Markdown > 自定义容器")]),_:1})])])])]),P,l("ul",null,[z,E,l("li",null,[A,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/copy-code.html"},{default:o(()=>[n("@vuepress/plugin-copy-code")]),_:1}),n(" 。")]),T])]),B,l("ul",null,[w,L,l("li",null,[N,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/external-link-icon.html"},{default:o(()=>[n("@vuepress/plugin-external-link-icon")]),_:1}),n(" 。")])])]),O,l("ul",null,[V,F,l("li",null,[R,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/git.html"},{default:o(()=>[n("@vuepress/plugin-git")]),_:1}),n(" 。")])])]),S,l("ul",null,[G,I,l("li",null,[H,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/medium-zoom.html"},{default:o(()=>[n("@vuepress/plugin-medium-zoom")]),_:1}),n(" 。")])])]),M,l("ul",null,[Z,j,l("li",null,[q,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/nprogress.html"},{default:o(()=>[n("@vuepress/plugin-nprogress")]),_:1}),n(" 。")])])]),J,l("ul",null,[K,Q,l("li",null,[U,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/seo/"},{default:o(()=>[n("@vuepress/plugin-seo")]),_:1}),n(" 。")]),W])]),X,l("ul",null,[Y,$,l("li",null,[ll,l("p",null,[n("是否启用 "),s(e,{to:"/zh/plugins/sitemap/"},{default:o(()=>[n("@vuepress/plugin-sitemap")]),_:1}),n(" 。")]),nl])])])}const il=i(c,[["render",el],["__file","plugin.html.vue"]]);export{il as default}; diff --git a/assets/prismjs.html-5BFkHxch.js b/assets/prismjs.html-5BFkHxch.js new file mode 100644 index 0000000000..b5359a95ab --- /dev/null +++ b/assets/prismjs.html-5BFkHxch.js @@ -0,0 +1,11 @@ +import{_ as i,r as l,o as t,c as p,a as e,b as s,d as n,e as r}from"./app-GUhkEPRO.js";const c={},d=s("h1",{id:"prismjs",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#prismjs"},[s("span",null,"prismjs")])],-1),u={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},h=r(`

    This plugin has been integrated into the default theme.

    Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself.

    Usage

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

    Options

    preloadLanguages

    `,7),m=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string[]")])],-1),g=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['markdown', 'jsdoc', 'yaml']")])],-1),D=s("p",null,"Details:",-1),v=s("p",null,"Languages to preload.",-1),_=s("p",null,"By default, languages will be loaded on demand when parsing markdown files.",-1),y={href:"https://github.com/PrismJS/prism/issues/2716",target:"_blank",rel:"noopener noreferrer"};function b(f,C){const o=l("NpmBadge"),a=l("ExternalLinkIcon");return t(),p("div",null,[d,e(o,{package:"@vuepress/plugin-prismjs"}),s("p",null,[n("This plugin will enable syntax highlighting for markdown code fence with "),s("a",u,[n("Prism.js"),e(a)]),n(".")]),h,s("ul",null,[m,g,s("li",null,[D,v,_,s("p",null,[n("However, Prism.js has "),s("a",y,[n("some potential issues"),e(a)]),n(" about loading languages dynamically. To avoid them, you can preload languages via this option.")])])])])}const x=i(c,[["render",b],["__file","prismjs.html.vue"]]);export{x as default}; diff --git a/assets/prismjs.html-DIgNkqCq.js b/assets/prismjs.html-DIgNkqCq.js new file mode 100644 index 0000000000..dae3176a4e --- /dev/null +++ b/assets/prismjs.html-DIgNkqCq.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-fbc77552","path":"/plugins/prismjs.html","title":"prismjs","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"preloadLanguages","slug":"preloadlanguages","link":"#preloadlanguages","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/prismjs.md"}');export{e as data}; diff --git a/assets/prismjs.html-G8UKOPdU.js b/assets/prismjs.html-G8UKOPdU.js new file mode 100644 index 0000000000..a8d82a6f68 --- /dev/null +++ b/assets/prismjs.html-G8UKOPdU.js @@ -0,0 +1,11 @@ +import{_ as r,r as l,o as i,c as o,a,b as s,d as n,e as c}from"./app-GUhkEPRO.js";const t={},d=s("h1",{id:"prismjs",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#prismjs"},[s("span",null,"prismjs")])],-1),u={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},m=c(`

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

    需要注意的是,该插件仅会给代码块添加 HTML 标记,而不会添加样式。当你在一个自定义主题中使用它时,可能需要自己选择并引入 Prism.js 样式主题。

    使用方法

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

    配置项

    preloadLanguages

    `,7),h=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string[]")])],-1),D=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['markdown', 'jsdoc', 'yaml']")])],-1),_=s("p",null,"详情:",-1),v=s("p",null,"需要预加载的语言。",-1),b=s("p",null,"默认情况下,语言会在解析 Markdown 文件时按需加载。",-1),g={href:"https://github.com/PrismJS/prism/issues/2716",target:"_blank",rel:"noopener noreferrer"};function y(C,f){const p=l("NpmBadge"),e=l("ExternalLinkIcon");return i(),o("div",null,[d,a(p,{package:"@vuepress/plugin-prismjs"}),s("p",null,[n("该插件使用 "),s("a",u,[n("Prism.js"),a(e)]),n(" 来为 Markdown 代码块启用代码高亮。")]),m,s("ul",null,[h,D,s("li",null,[_,v,b,s("p",null,[n("然而, Prism.js 在动态加载语言时可能会遇到 "),s("a",g,[n("一些潜在的问题"),a(e)]),n(" 。为了避免这些问题,你可以使用该配置项来预加载一些语言。")])])])])}const x=r(t,[["render",y],["__file","prismjs.html.vue"]]);export{x as default}; diff --git a/assets/prismjs.html-GY0lJvRB.js b/assets/prismjs.html-GY0lJvRB.js new file mode 100644 index 0000000000..3520e931ce --- /dev/null +++ b/assets/prismjs.html-GY0lJvRB.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-ad678bf4","path":"/zh/plugins/prismjs.html","title":"prismjs","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"preloadLanguages","slug":"preloadlanguages","link":"#preloadlanguages","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/prismjs.md"}');export{e as data}; diff --git a/assets/pwa-popup.html-4su7q40y.js b/assets/pwa-popup.html-4su7q40y.js new file mode 100644 index 0000000000..8070ca1bb2 --- /dev/null +++ b/assets/pwa-popup.html-4su7q40y.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-7bd28997","path":"/zh/plugins/pwa-popup.html","title":"pwa-popup","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/pwa-popup.md"}');export{l as data}; diff --git a/assets/pwa-popup.html-_RkFLcjG.js b/assets/pwa-popup.html-_RkFLcjG.js new file mode 100644 index 0000000000..5955d6e121 --- /dev/null +++ b/assets/pwa-popup.html-_RkFLcjG.js @@ -0,0 +1,40 @@ +import{_ as c,r as p,o as t,c as r,a,b as s,d as n,w as e,e as o}from"./app-GUhkEPRO.js";const D={},d=s("h1",{id:"pwa-popup",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa-popup"},[s("span",null,"pwa-popup")])],-1),u=s("p",null,"Provide a popup component for users to activate the new PWA service worker manually.",-1),y=s("code",null,"skipWaiting",-1),v=s("code",null,"true",-1),C=o(`

    When the new service worker is ready, a popup will appear in the right bottom of the page to ask users to activate the waiting service worker.

    Usage

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

    Options

    locales

    • Type: Record<string, { message: string, buttonText: string }>

    • Details:

      The messages of the popup in different locales.

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

    • Example:

    export default {
    +  plugins: [
    +    pwaPlugin(),
    +    pwaPopupPlugin({
    +      locales: {
    +        '/': {
    +          message: 'New content is available.',
    +          buttonText: 'Refresh',
    +        },
    +        '/zh/': {
    +          message: '发现新内容可用',
    +          buttonText: '刷新',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,8),m=o(`

    Styles

    You can customize the style of the popup via CSS variables:

    :root {
    +  --pwa-popup-z-index: 10;
    +  --pwa-popup-text-color: #2c3e50;
    +  --pwa-popup-bg-color: #ffffff;
    +  --pwa-popup-border-color: #3eaf7c;
    +  --pwa-popup-shadow: 0 4px 16px var(--pwa-popup-border-color);
    +  --pwa-popup-btn-text-color: #ffffff;
    +  --pwa-popup-btn-bg-color: #3eaf7c;
    +  --pwa-popup-btn-hover-bg-color: #4abf8a;
    +}
    +
    `,3);function b(h,E){const i=p("NpmBadge"),l=p("RouterLink");return t(),r("div",null,[d,a(i,{package:"@vuepress/plugin-pwa-popup"}),u,s("p",null,[n("This plugin must be used together with "),a(l,{to:"/plugins/pwa.html"},{default:e(()=>[n("pwa plugin")]),_:1}),n(", and the "),y,n(" option must not be set to "),v,n(".")]),C,s("ul",null,[s("li",null,[n("Also see: "),s("ul",null,[s("li",null,[a(l,{to:"/guide/i18n.html"},{default:e(()=>[n("Guide > I18n")]),_:1})])])])]),m])}const f=c(D,[["render",b],["__file","pwa-popup.html.vue"]]);export{f as default}; diff --git a/assets/pwa-popup.html-tGZDCjGz.js b/assets/pwa-popup.html-tGZDCjGz.js new file mode 100644 index 0000000000..48d44a31ed --- /dev/null +++ b/assets/pwa-popup.html-tGZDCjGz.js @@ -0,0 +1,40 @@ +import{_ as i,r as p,o as r,c as t,a,b as s,d as n,w as e,e as o}from"./app-GUhkEPRO.js";const D={},d=s("h1",{id:"pwa-popup",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa-popup"},[s("span",null,"pwa-popup")])],-1),u=s("p",null,"提供一个弹窗组件,允许用户手动刷新 PWA Service Worker 。",-1),y=s("code",null,"skipWaiting",-1),v=s("code",null,"true",-1),C=o(`

    当新的 Service Worker 就绪时,会在页面右下角出现一个弹窗,询问用户是否需要激活处于 Waiting 状态的 Service Worker 。

    使用方法

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

    配置项

    locales

    • 类型: Record<string, { message: string, buttonText: string }>

    • 详情:

      弹窗在不同 locales 下的信息。

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

    • 示例:

    export default {
    +  plugins: [
    +    pwaPlugin(),
    +    pwaPopupPlugin({
    +      locales: {
    +        '/': {
    +          message: 'New content is available.',
    +          buttonText: 'Refresh',
    +        },
    +        '/zh/': {
    +          message: '发现新内容可用',
    +          buttonText: '刷新',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,8),b=o(`

    样式

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

    :root {
    +  --pwa-popup-z-index: 10;
    +  --pwa-popup-text-color: #2c3e50;
    +  --pwa-popup-bg-color: #ffffff;
    +  --pwa-popup-border-color: #3eaf7c;
    +  --pwa-popup-shadow: 0 4px 16px var(--pwa-popup-border-color);
    +  --pwa-popup-btn-text-color: #ffffff;
    +  --pwa-popup-btn-bg-color: #3eaf7c;
    +  --pwa-popup-btn-hover-bg-color: #4abf8a;
    +}
    +
    `,3);function m(h,E){const c=p("NpmBadge"),l=p("RouterLink");return r(),t("div",null,[d,a(c,{package:"@vuepress/plugin-pwa-popup"}),u,s("p",null,[n("该插件必须和 "),a(l,{to:"/zh/plugins/pwa.html"},{default:e(()=>[n("pwa 插件")]),_:1}),n(" 一起使用,并且 "),y,n(" 配置项不能设置为 "),v,n(" 。")]),C,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[a(l,{to:"/guide/i18n.html"},{default:e(()=>[n("指南 > 多语言支持")]),_:1})])])])]),b])}const f=i(D,[["render",m],["__file","pwa-popup.html.vue"]]);export{f as default}; diff --git a/assets/pwa-popup.html-vgfvPd3T.js b/assets/pwa-popup.html-vgfvPd3T.js new file mode 100644 index 0000000000..51a45afc3a --- /dev/null +++ b/assets/pwa-popup.html-vgfvPd3T.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-60cd04a8","path":"/plugins/pwa-popup.html","title":"pwa-popup","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/pwa-popup.md"}');export{e as data}; diff --git a/assets/pwa.html-9r2A4uTL.js b/assets/pwa.html-9r2A4uTL.js new file mode 100644 index 0000000000..30f5b5c14c --- /dev/null +++ b/assets/pwa.html-9r2A4uTL.js @@ -0,0 +1,68 @@ +import{_ as r,r as t,o as c,c as d,a as e,b as s,d as n,w as p,e as l}from"./app-GUhkEPRO.js";const D={},u=s("h1",{id:"pwa",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa"},[s("span",null,"pwa")])],-1),y={href:"https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},v={href:"https://developers.google.com/web/tools/workbox/modules/workbox-build",target:"_blank",rel:"noopener noreferrer"},m={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},h={class:"custom-container warning"},b=s("p",{class:"custom-container-title"},"WARNING",-1),C=l(`

    Usage

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

    Web App Manifests

    `,4),g={href:"https://developer.mozilla.org/en-US/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},_=s("code",null,".vuepress/public",-1),f=l(`
    1. Create manifest file

    Typically .vuepress/public/manifest.webmanifest:

    {
    +  "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"
    +    }
    +  ]
    +}
    +
    1. Generate PWA icons

    To make your PWA more accessible, you need to generate some icons, and put them inside the public directory.

    Make sure the path of icons matches the icons field in your manifest file:

    • .vuepress/public/images/icons/android-chrome-192x192.png
    • .vuepress/public/images/icons/android-chrome-384x384.png
    `,7),E={class:"custom-container tip"},k=s("p",{class:"custom-container-title"},"TIP",-1),w={href:"https://realfavicongenerator.net/",target:"_blank",rel:"noopener noreferrer"},x=s("ol",{start:"3"},[s("li",null,"Set tags in head")],-1),q={href:"https://developer.mozilla.org/en-US/docs/Web/Manifest#deploying_a_manifest_with_the_link_tag",target:"_blank",rel:"noopener noreferrer"},F=l(`
    export default {
    +  head: [
    +    ['link', { rel: 'manifest', href: '/manifest.webmanifest' }],
    +    ['meta', { name: 'theme-color', content: '#3eaf7c' }],
    +    // ...other tags
    +  ],
    +}
    +

    Options

    `,2),A={href:"https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW",target:"_blank",rel:"noopener noreferrer"},W=s("code",null,"globDirectory",-1),P=s("code",null,"swDest",-1),S=l(`

    For example, you can set skipWaiting: true to auto activate the new service worker once it is ready:

    export default {
    +  plugins: [
    +    pwaPlugin({
    +      skipWaiting: true,
    +    }),
    +  ],
    +}
    +

    But if you omit skipWaiting or set it to false, you have to activate the new service worker manually:

    `,3),T=s("li",null,[n("For developers, you can use our "),s("a",{href:"#composition-api"},"composition API"),n(" to take control of the service worker behavior.")],-1),I=s("h3",{id:"serviceworkerfilename",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#serviceworkerfilename"},[s("span",null,"serviceWorkerFilename")])],-1),N=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string")])],-1),V=s("li",null,[s("p",null,[n("Default: "),s("code",null,"'service-worker.js'")])],-1),z=s("p",null,"Details:",-1),B=s("p",null,[n("The service worker file will only be generated in "),s("code",null,"build"),n(" mode.")],-1),G=s("h2",{id:"composition-api",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#composition-api"},[s("span",null,"Composition API")])],-1),M=s("h3",{id:"usepwaevent",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#usepwaevent"},[s("span",null,"usePwaEvent")])],-1),R=s("p",null,"Details:",-1),U=s("p",null,"Returns the event emitter of this plugin.",-1),j={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},L=s("li",null,[s("p",null,"Example:")],-1),Y=l(`
    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker is active.')
    +    })
    +  },
    +}
    +

    useSkipWaiting

    • Parameters:
    ParameterTypeDescription
    registrationServiceWorkerRegistrationThe registration of the service worker you want activate
    `,4),O=s("p",null,"Details:",-1),H={href:"https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting",target:"_blank",rel:"noopener noreferrer"},J=s("li",null,[s("p",null,"Example:")],-1),K=l(`
    import { usePwaEvent, useSkipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('updated', (registration) => {
    +      console.log('The waiting service worker is available.')
    +      // activate the waiting service worker
    +      useSkipWaiting(registration)
    +    })
    +  },
    +}
    +
    `,1);function Q(X,Z){const i=t("NpmBadge"),a=t("ExternalLinkIcon"),o=t("RouterLink");return c(),d("div",null,[u,e(i,{package:"@vuepress/plugin-pwa"}),s("p",null,[n("Make your VuePress site a "),s("a",y,[n("Progressive Web Application (PWA)"),e(a)]),n(".")]),s("p",null,[n("This plugin uses "),s("a",v,[n("workbox-build"),e(a)]),n(" to generate service worker file, and uses "),s("a",m,[n("register-service-worker"),e(a)]),n(" to register service worker.")]),s("div",h,[b,s("p",null,[n("If you enabled this plugin once and you want to disable it, you might need "),e(o,{to:"/plugins/remove-pwa.html"},{default:p(()=>[n("`@vuepress/plugin-remove-pwa")]),_:1}),n(" to remove the existing service worker.")])]),C,s("p",null,[n("To make your website fully compliant with PWA, you need to create a "),s("a",g,[n("Web app manifests"),e(a)]),n(" file and set the icons, colors, etc. for your PWA.")]),s("p",null,[n("You need to put your manifest file and icons into the "),e(o,{to:"/guide/assets.html#public-files"},{default:p(()=>[n("public files directory")]),_:1}),n(". In the following example, we assume that you are using the default public directory "),_,n(".")]),f,s("div",E,[k,s("p",null,[n("Some tools can help to do that. For example, "),s("a",w,[n("Favicon Generator"),e(a)]),n(" would help you to generate icons together with a sample manifest file.")])]),x,s("p",null,[n("You also need to set some tags via "),e(o,{to:"/config.html#head"},{default:p(()=>[n("head")]),_:1}),n(" option to "),s("a",q,[n("deploy the manifest"),e(a)]),n(":")]),F,s("p",null,[n("This plugin accepts all parameters of workbox-build's "),s("a",A,[n("generateSW method"),e(a)]),n(" in its options, except "),W,n(" and "),P,n(".")]),S,s("ul",null,[s("li",null,[n("For users, you can use our "),e(o,{to:"/plugins/pwa-popup.html"},{default:p(()=>[n("pwa-popup")]),_:1}),n(" plugin together.")]),T]),I,s("ul",null,[N,V,s("li",null,[z,s("p",null,[n("File path of the generated service worker file, which is relative to the "),e(o,{to:"/config.html#dest"},{default:p(()=>[n("dest")]),_:1}),n(" directory.")]),B])]),G,M,s("ul",null,[s("li",null,[R,U,s("p",null,[n("You can add listener function to events that provided by "),s("a",j,[n("register-service-worker"),e(a)]),n(".")])]),L]),Y,s("ul",null,[s("li",null,[O,s("p",null,[n("Call "),s("a",H,[n("skipWaiting()"),e(a)]),n(" to activate the waiting service worker.")])]),J]),K])}const ss=r(D,[["render",Q],["__file","pwa.html.vue"]]);export{ss as default}; diff --git a/assets/pwa.html-Brwoz8J_.js b/assets/pwa.html-Brwoz8J_.js new file mode 100644 index 0000000000..76fcc0a205 --- /dev/null +++ b/assets/pwa.html-Brwoz8J_.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7da23f32","path":"/plugins/pwa.html","title":"pwa","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Web App Manifests","slug":"web-app-manifests","link":"#web-app-manifests","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"serviceWorkerFilename","slug":"serviceworkerfilename","link":"#serviceworkerfilename","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"usePwaEvent","slug":"usepwaevent","link":"#usepwaevent","children":[]},{"level":3,"title":"useSkipWaiting","slug":"useskipwaiting","link":"#useskipwaiting","children":[]}]}],"git":{"updatedTime":1706931533000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"plugins/pwa.md"}');export{e as data}; diff --git a/assets/pwa.html-h9cZRfgZ.js b/assets/pwa.html-h9cZRfgZ.js new file mode 100644 index 0000000000..5fdecbe873 --- /dev/null +++ b/assets/pwa.html-h9cZRfgZ.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5c3a8496","path":"/zh/plugins/pwa.html","title":"pwa","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"Web App Manifests","slug":"web-app-manifests","link":"#web-app-manifests","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"serviceWorkerFilename","slug":"serviceworkerfilename","link":"#serviceworkerfilename","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"usePwaEvent","slug":"usepwaevent","link":"#usepwaevent","children":[]},{"level":3,"title":"useSkipWaiting","slug":"useskipwaiting","link":"#useskipwaiting","children":[]}]}],"git":{"updatedTime":1706931533000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":2}]},"filePathRelative":"zh/plugins/pwa.md"}');export{e as data}; diff --git a/assets/pwa.html-p5uqsdnO.js b/assets/pwa.html-p5uqsdnO.js new file mode 100644 index 0000000000..eba6cb4f10 --- /dev/null +++ b/assets/pwa.html-p5uqsdnO.js @@ -0,0 +1,68 @@ +import{_ as r,r as t,o as c,c as d,a as e,b as s,d as n,w as p,e as l}from"./app-GUhkEPRO.js";const D={},u=s("h1",{id:"pwa",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#pwa"},[s("span",null,"pwa")])],-1),v={href:"https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps",target:"_blank",rel:"noopener noreferrer"},y={href:"https://developers.google.com/web/tools/workbox/modules/workbox-build",target:"_blank",rel:"noopener noreferrer"},m={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},C={class:"custom-container warning"},b=s("p",{class:"custom-container-title"},"注意",-1),h=l(`

    使用方法

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

    Web App Manifests

    `,4),_={href:"https://developer.mozilla.org/zh-CN/docs/Web/Manifest",target:"_blank",rel:"noopener noreferrer"},g=s("code",null,".vuepress/public",-1),E=l(`
    1. 创建 Manifest 文件

    通常是 .vuepress/public/manifest.webmanifest

    {
    +  "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"
    +    }
    +  ]
    +}
    +
    1. 生成 PWA 图标

    为了提高你的 PWA 的可用性,你需要生成一些图标,并将它们放置在 Public 目录下。

    确保图标的路径匹配 Manifest 文件中的 icons 字段:

    • .vuepress/public/images/icons/android-chrome-192x192.png
    • .vuepress/public/images/icons/android-chrome-384x384.png
    `,7),k={class:"custom-container tip"},f=s("p",{class:"custom-container-title"},"提示",-1),q={href:"https://realfavicongenerator.net/",target:"_blank",rel:"noopener noreferrer"},w=s("ol",{start:"3"},[s("li",null,"设置 Head 中的标签")],-1),x={href:"https://developer.mozilla.org/en-US/docs/Web/Manifest#deploying_a_manifest_with_the_link_tag",target:"_blank",rel:"noopener noreferrer"},F=l(`
    export default {
    +  head: [
    +    ['link', { rel: 'manifest', href: '/manifest.webmanifest' }],
    +    ['meta', { name: 'theme-color', content: '#3eaf7c' }],
    +    // ...其他标签
    +  ],
    +}
    +

    配置项

    `,2),W={href:"https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW",target:"_blank",rel:"noopener noreferrer"},A=s("code",null,"globDirectory",-1),S=s("code",null,"swDest",-1),P=l(`

    比如,你可以设置 skipWaiting: true ,这将在新的 Service Worker 就绪之后立即激活它:

    export default {
    +  plugins: [
    +    pwaPlugin({
    +      skipWaiting: true,
    +    }),
    +  ],
    +}
    +

    但是如果你不设置 skipWaiting 或设置为 false ,你就需要手动激活新的 Service Worker 。

    `,3),z=s("li",null,[n("对于开发者,你可以使用该插件提供的 "),s("a",{href:"#composition-api"},"Composition API"),n(" 来控制 Service Worker 的行为。")],-1),B=s("h3",{id:"serviceworkerfilename",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#serviceworkerfilename"},[s("span",null,"serviceWorkerFilename")])],-1),M=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string")])],-1),N=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"'service-worker.js'")])],-1),V=s("p",null,"详情:",-1),I=s("p",null,[n("Service Worker 文件只会在 "),s("code",null,"build"),n(" 模式下生成。")],-1),j=s("h2",{id:"composition-api",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#composition-api"},[s("span",null,"Composition API")])],-1),L=s("h3",{id:"usepwaevent",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#usepwaevent"},[s("span",null,"usePwaEvent")])],-1),R=s("p",null,"详情:",-1),G=s("p",null,"返回该插件的 Event Emitter 。",-1),U={href:"https://github.com/yyx990803/register-service-worker",target:"_blank",rel:"noopener noreferrer"},H=s("li",null,[s("p",null,"示例:")],-1),T=l(`
    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker 已经生效。')
    +    })
    +  },
    +}
    +

    useSkipWaiting

    • 参数:
    参数类型描述
    registrationServiceWorkerRegistration你想要激活的 Service Worker 的 Registration
    `,4),J=s("p",null,"详情:",-1),K={href:"https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/skipWaiting",target:"_blank",rel:"noopener noreferrer"},O=s("li",null,[s("p",null,"示例:")],-1),Q=l(`
    import { usePwaEvent, useSkipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('updated', (registration) => {
    +      console.log('在 Waiting 状态的 Service Worker 已经就绪。')
    +      // 激活 Waiting 状态的 Service Worker
    +      useSkipWaiting(registration)
    +    })
    +  },
    +}
    +
    `,1);function X(Y,Z){const i=t("NpmBadge"),a=t("ExternalLinkIcon"),o=t("RouterLink");return c(),d("div",null,[u,e(i,{package:"@vuepress/plugin-pwa"}),s("p",null,[n("使你的 VuePress 站点成为一个 "),s("a",v,[n("渐进式 Web 应用 (PWA)"),e(a)]),n(".")]),s("p",null,[n("该插件使用 "),s("a",y,[n("workbox-build"),e(a)]),n(" 来生成 Service Worker 文件,并通过 "),s("a",m,[n("register-service-worker"),e(a)]),n(" 来注册 Service Worker 。")]),s("div",C,[b,s("p",null,[n("如果你启用过该插件,并想要禁用它,你可能需要 "),e(o,{to:"/zh/plugins/remove-pwa.html"},{default:p(()=>[n("`@vuepress/plugin-remove-pwa")]),_:1}),n(" 来移除现有的 Service Worker 。")])]),h,s("p",null,[n("为了使你的网站符合 PWA 的要求,你需要创建一个 "),s("a",_,[n("Web app manifests"),e(a)]),n(" 文件,并且为你的 PWA 设置图标、颜色等信息。")]),s("p",null,[n("你需要将你的 Manifest 文件和图标放置在 "),e(o,{to:"/guide/assets.html#public-%E6%96%87%E4%BB%B6"},{default:p(()=>[n("Public 目录")]),_:1}),n(" 下。在下述的示例中,我们假设你正在使用默认的 Public 目录 "),g,n(" 。")]),E,s("div",k,[f,s("p",null,[n("一些工具可以帮助你做这些事。比如 "),s("a",q,[n("Favicon Generator"),e(a)]),n(" 可以帮助你生成图片以及一个 Manifest 文件样例。")])]),w,s("p",null,[n("你还需要通过 "),e(o,{to:"/zh/config.html#head"},{default:p(()=>[n("head")]),_:1}),n(" 配置项来设置一些标签,用来 "),s("a",x,[n("部署你的 Manifest 文件"),e(a)]),n(" 。")]),F,s("p",null,[n("该插件的配置项可以接收 workbox-build 中 "),s("a",W,[n("generateSW 方法"),e(a)]),n(" 除了 "),A,n(" 和 "),S,n(" 以外的所有参数。")]),P,s("ul",null,[s("li",null,[n("对于用户,你可以配合我们提供的 "),e(o,{to:"/zh/plugins/pwa-popup.html"},{default:p(()=>[n("pwa-popup")]),_:1}),n(" 插件一起使用。")]),z]),B,s("ul",null,[M,N,s("li",null,[V,s("p",null,[n("生成的 Service Worker 文件路径,该路径是 "),e(o,{to:"/zh/config.html#dest"},{default:p(()=>[n("dest")]),_:1}),n(" 目录的相对路径。")]),I])]),j,L,s("ul",null,[s("li",null,[R,G,s("p",null,[n("你可以为 "),s("a",U,[n("register-service-worker"),e(a)]),n(" 提供的事件添加事件监听器。")])]),H]),T,s("ul",null,[s("li",null,[J,s("p",null,[n("调用 "),s("a",K,[n("skipWaiting()"),e(a)]),n(" 来激活处于 Waiting 状态的 Service Worker 。")])]),O]),Q])}const ss=r(D,[["render",X],["__file","pwa.html.vue"]]);export{ss as default}; diff --git a/assets/reading-time.html--6neugEt.js b/assets/reading-time.html--6neugEt.js new file mode 100644 index 0000000000..08208f46d0 --- /dev/null +++ b/assets/reading-time.html--6neugEt.js @@ -0,0 +1,95 @@ +import{_ as a,r as l,o as e,c as p,a as i,b as s,e as o}from"./app-GUhkEPRO.js";const c={},r=s("h1",{id:"reading-time",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#reading-time"},[s("span",null,"reading-time")])],-1),t=o(`

    此插件会为每个页面生成字数统计与预计阅读时间。

    使用方法

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

    插件会将相关信息注入到页面数据的 readingTime,其中:

    • readingTime.minutes:为预计阅读时间(分钟)number
    • readingTime.words:字数统计,number

    在 Node 侧获取数据

    对于任何页面,你可以从 page.data.readingTime 获取预计阅读时间与字数统计:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    你可以在 extendsPage 以及其他生命周期获取它做进一步处理:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    在客户端侧获取数据

    你可以从 @vuepress/plugin-reading-time/client 导入 useReadingTimeDatauseReadingTimeLocale 来获取当前页面的阅读时间数据和语言环境数据:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 分钟", words: "100 字" }
    +</script>
    +

    选项

    wordPerMinute

    • 类型:number
    • 默认值:300
    • 详情: 每分钟阅读字数

    locales

    • 类型:ReadingTimeLocaleConfig

      interface ReadingTimeLocaleData {
      +  /**
      +   * 字数模板,模板中 \`$word\` 会被自动替换为字数
      +   */
      +  word: string
      +
      +  /**
      +   * 小于一分钟文字
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * 时间模板
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimeLocaleConfig {
      +  [localePath: string]: ReadingTimeLocaleData
      +}
      +
    • 必填:否

    • 详情:

      阅读时间插件的国际化配置。

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

    客户端 API

    你可以从 @vuepress/plugin-reading-time/client 导入并使用这些 API:

    即使插件被禁用,这些 API 也不会抛出错误。

    useReadingTimeData

    interface ReadingTime {
    +  /** 分钟为单位的预计阅读时长 */
    +  minutes: number
    +  /** 内容的字数 */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    当插件被禁用时会返回 null

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** 当前语言的预计阅读时间 */
    +  time: string
    +  /** 当前语言的字数文字 */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    高级使用

    由于此插件主要面向插件和主题开发者,所以提供了 "使用 API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    为什么你应该使用 "使用 API"

    1. 当你多次注册一个插件时,vuepress 会给你一个警告,告诉你只有第一个插件会生效。useReadingTimePlugin 会自动检测插件是否已经注册,避免多次注册。

    2. 如果你在 extendsPage 生命周期访问阅读时间数据,那么 @vuepress/plugin-reading-time 必须在你的主题或插件之前被调用,否则你会得到未定义的 page.data.readingTimeuseReadingTimePlugin 确保了 @vuepress/plugin-reading-time 在你的主题或插件之前被调用。

    我们也提供了一个 removeReadingTimePlugin api 来移除插件。你可以使用它来确保你的调用生效或清除插件:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // 这会移除任何当前存在的阅读时间插件
    +  removeReadingTimePlugin(app)
    +
    +  // 所以这会生效,即使之前已经注册了一个阅读时间插件
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    `,34);function d(D,u){const n=l("NpmBadge");return e(),p("div",null,[r,i(n,{package:"@vuepress/plugin-reading-time"}),t])}const v=a(c,[["render",d],["__file","reading-time.html.vue"]]);export{v as default}; diff --git a/assets/reading-time.html-8A3DmWWk.js b/assets/reading-time.html-8A3DmWWk.js new file mode 100644 index 0000000000..bccf7f5d2e --- /dev/null +++ b/assets/reading-time.html-8A3DmWWk.js @@ -0,0 +1,95 @@ +import{_ as a,r as e,o as l,c as i,a as p,b as s,e as o}from"./app-GUhkEPRO.js";const t={},r=s("h1",{id:"reading-time",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#reading-time"},[s("span",null,"reading-time")])],-1),c=o(`

    This plugin will generate word count and estimated reading time for each page.

    Usage

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

    The plugin will inject reading time information into the readingTime of the page data, where:

    • readingTime.minutes: estimated reading time (minutes) number
    • readingTime.words: word count number

    Getting data on Node Side

    For any page, you can get estimated reading time and word count from page.data.readingTime:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    You can access it for further processing in the extendsPage lifecycle and other lifecycle:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    Getting data on Client Side

    You can import useReadingTimeData and useReadingTimeLocale from @vuepress/plugin-reading-time/client to get the reading time data and locale data of the current page:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 minute", words: "100 words" }
    +</script>
    +

    Options

    wordPerMinute

    • Type: number
    • Default: 300
    • Details: Reading speed (words per minute)

    locales

    • Type: ReadingTimeLocaleConfig

      interface ReadingTimeLocaleData {
      +  /**
      +   * Word template, \`$word\` will be automatically replaced by actual words
      +   */
      +  word: string
      +
      +  /**
      +   * Text for less than one minute
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * Time template
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimeLocaleConfig {
      +  [localePath: string]: ReadingTimeLocaleData
      +}
      +
    • Required: No

    • Details:

      Locales config for reading-time plugin.

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

    Client API

    You can import and use these APIs from @vuepress/plugin-reading-time/client:

    These APIs won't throw even you disable the plugin.

    useReadingTimeData

    interface ReadingTime {
    +  /** Expect reading time in minute unit */
    +  minutes: number
    +  /** Words count of content */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    null is returned when the plugin is disabled.

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** Expect reading time text in locale */
    +  time: string
    +  /** Word count text in locale */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    Advanced Usage

    This plugin is targeting plugin and theme developers mostly, so we provide a "Use API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    Why you should use "Use API"

    1. When you register a plugin multiple times, vuepress will gives you warning about that telling you only the first one takes effect. The useReadingTimePlugin automatically detects if the plugin is registered and avoid registering multiple times.
    2. If you access reading time data in extendsPage lifecycle, then @vuepress/plugin-reading-time must be called before your theme or plugin, otherwise you will get undefined for page.data.readingTime. The useReadingTimePlugin ensures that @vuepress/plugin-reading-time is called before your theme or plugin.

    We also provides a removeReadingTimePlugin api to remove the plugin.You can use this to ensure your call take effect or clear the plugin:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // this removes any existing reading time plugin at this time
    +  removeReadingTimePlugin(app)
    +
    +  // so this will take effect even if there is a reading time plugin registered before
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    `,34);function d(D,u){const n=e("NpmBadge");return l(),i("div",null,[r,p(n,{package:"@vuepress/plugin-reading-time"}),c])}const m=a(t,[["render",d],["__file","reading-time.html.vue"]]);export{m as default}; diff --git a/assets/reading-time.html-pIyUC7pP.js b/assets/reading-time.html-pIyUC7pP.js new file mode 100644 index 0000000000..e08b46d4e9 --- /dev/null +++ b/assets/reading-time.html-pIyUC7pP.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-549eb354","path":"/zh/plugins/reading-time.html","title":"reading-time","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"在 Node 侧获取数据","slug":"在-node-侧获取数据","link":"#在-node-侧获取数据","children":[]},{"level":3,"title":"在客户端侧获取数据","slug":"在客户端侧获取数据","link":"#在客户端侧获取数据","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"wordPerMinute","slug":"wordperminute","link":"#wordperminute","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"客户端 API","slug":"客户端-api","link":"#客户端-api","children":[{"level":3,"title":"useReadingTimeData","slug":"usereadingtimedata","link":"#usereadingtimedata","children":[]},{"level":3,"title":"useReadingTimeLocale","slug":"usereadingtimelocale","link":"#usereadingtimelocale","children":[]}]},{"level":2,"title":"高级使用","slug":"高级使用","link":"#高级使用","children":[]}],"git":{"updatedTime":1706806032000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/reading-time.md"}');export{e as data}; diff --git a/assets/reading-time.html-r9J-dZBq.js b/assets/reading-time.html-r9J-dZBq.js new file mode 100644 index 0000000000..bc84f0b55e --- /dev/null +++ b/assets/reading-time.html-r9J-dZBq.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6553daba","path":"/plugins/reading-time.html","title":"reading-time","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Getting data on Node Side","slug":"getting-data-on-node-side","link":"#getting-data-on-node-side","children":[]},{"level":3,"title":"Getting data on Client Side","slug":"getting-data-on-client-side","link":"#getting-data-on-client-side","children":[]}]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"wordPerMinute","slug":"wordperminute","link":"#wordperminute","children":[]},{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]}]},{"level":2,"title":"Client API","slug":"client-api","link":"#client-api","children":[{"level":3,"title":"useReadingTimeData","slug":"usereadingtimedata","link":"#usereadingtimedata","children":[]},{"level":3,"title":"useReadingTimeLocale","slug":"usereadingtimelocale","link":"#usereadingtimelocale","children":[]}]},{"level":2,"title":"Advanced Usage","slug":"advanced-usage","link":"#advanced-usage","children":[]}],"git":{"updatedTime":1706806032000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/reading-time.md"}');export{e as data}; diff --git a/assets/redirect.html-3IGFLCWJ.js b/assets/redirect.html-3IGFLCWJ.js new file mode 100644 index 0000000000..0c5d6b6e7c --- /dev/null +++ b/assets/redirect.html-3IGFLCWJ.js @@ -0,0 +1,71 @@ +import{_ as a,r as n,o as l,c as o,a as t,b as s,e as i}from"./app-GUhkEPRO.js";const p={},c=s("h1",{id:"redirect",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redirect"},[s("span",null,"redirect")])],-1),r=i(`

    This plugin can automatically handle redirects for your site.

    Usage

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

    Control Page Redirection

    If you change the address of an existing page, you can use the redirectFrom option in Frontmatter to redirect to the address of this page, which ensures that users are redirected to the new address when they visit the old link.

    If you need to redirect an existing page to a new page, you can use the redirectTo option in Frontmatter to set the address to redirect to. This way the page will redirect to the new address when accessed.

    You can also set config with a redirect map in plugin options, see config for more details.

    Auto Locales

    The plugin can automatically redirect non-multilingual links to the multilingual pages the user needs based on the user's language preference.

    To achieve this, you need to leave the default language directory (/) blank and set autoLocale: true in plugin options. The plugin will automatically redirect to the correct page according to the user's language.

    I.E.: you need to set the following directory structure:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    And set locales in theme options with:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    So when a user accesses / or /page.html, they are automatically redirected to /en/ /en/page.html and /en/ /en/page.html based on current browser language.

    Customizing fallback behavior

    Sometimes, users may add more than one language to the system settings. By default, when a site supports a preferred language, but the page not exists for the preferred language, the plugin attempts to match the alternate language set by the user.

    If you don't need to fall back to the user's alternate language, but directly match the user's preferred language, set localeFallback: false in the plugin options.

    Customizing missing behavior

    Sometimes, when a user visits a page, the document does not yet contain the language version the user needs (a common case is that the current page has not been localized in the relevant language), so the plugin needs to perform a default action, which you can customize by defaultBehavior in the plugin options:

    • "defaultLocale": Redirect to default language or first available language page (default behavior)
    • "homepage": redirect to the home page in the current language (only available if the document contains the user's language)
    • "404": Redirect to page 404 in current language (only available if the document contains the user's language)

    Customizing default locale path

    You can customize the default locale path by setting defaultLocale in the plugin options. By default, the plugin uses the first locale key in locales as the default language.

    Automatically switch languages

    The plugin supports automatically switching the link to the multilingual page that the user needs according to the user's language preference when opening a multilingual document. In order to achieve this, you need to set switchLocale in the plugin options, which can be the following two values:

    • direct: switch directly to the user language preference page without asking
    • modal: When the user's language preference is different from the current page language, show a modal asking whether to switch language

    Customizing Locale Settings

    By default, the plugin generates a locale setting by reading locale path and lang from the site's locales option. Sometimes, you may want multiple languages to hit the same path, in which case you should set localeConfig in plugin options.

    For example, you might want all English users to match to /en/ and Chinese Traditional users to /zh/, then you can set:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    Redirecting Sites

    Sometimes you may change base or use new domain for your site, so you may want the original site automatically redirects to the new one.

    To solve this, the plugin provide vp-redirect cli.

    Usage:
    +  $ vp-redirect generate [sourceDir]
    +
    +Options:
    +  --hostname <hostname>  Hostname to redirect to (E.g.: https://new.example.com/) (default: /)
    +  -c, --config <config>  Set path to config file
    +  -o, --output <output>  Set the output directory (default: .vuepress/redirect)
    +  --cache <cache>        Set the directory of the cache files
    +  -t, --temp <temp>      Set the directory of the temporary files
    +  --clean-cache          Clean the cache files before generation
    +  --clean-temp           Clean the temporary files before generation
    +  -h, --help             Display this message
    +

    You need to pass in VuePress project source dir and also set the hostname option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory.

    By default, the plugin will output to .vuepress/redirect directory under source directory. And you should upload it to your original site to provide redirection.

    Plugin Options

    config

    • Type: Record<string, string> | ((app: App) => Record<string, string>)

    • Details: Redirect map.

    • Example:

      When base is set to /base/:

      • redirect /base/foo.html to /base/bar.html
      • /base/baz.html to https://example.com/qux.html.
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      Redirect post folder to posts folder:

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\\/posts\\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    • Type: boolean
    • Default: false
    • Details: Whether enable locales redirection.

    switchLocale

    • Type: "direct" | "modal" | false

    • Default: false

    • Details:

      Whether switch to a new locale based on user preference.

      • "direct": redirect to the new locale directly without asking
      • "modal": show a modal to let user choose whether to switch to the new locale

    localeConfig

    • Type: Record<string, string | string[]>

    • Details: Locale language config

    localeFallback

    • Type: boolean
    • Default: true
    • Details: Whether fallback to other locales user defined

    defaultBehavior

    • Type: "defaultLocale" | "homepage" | "404"
    • Default: "defaultLocale"
    • Details: Behavior when a locale version is not available for current link.

    defaultLocale

    • Type: string
    • Default: the first locale
    • Details: Default locale path.

    Frontmatter options

    redirectFrom

    • Type: string | string[]
    • Details: The link which this page redirects from.

    redirectTo

    • Type: string
    • Details: The link which this page redirects to.
    `,52);function d(u,D){const e=n("NpmBadge");return l(),o("div",null,[c,t(e,{package:"@vuepress/plugin-redirect"}),r])}const y=a(p,[["render",d],["__file","redirect.html.vue"]]);export{y as default}; diff --git a/assets/redirect.html-CCGsuo31.js b/assets/redirect.html-CCGsuo31.js new file mode 100644 index 0000000000..bad1dd8118 --- /dev/null +++ b/assets/redirect.html-CCGsuo31.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-6a3f6e16","path":"/plugins/redirect.html","title":"redirect","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[{"level":3,"title":"Control Page Redirection","slug":"control-page-redirection","link":"#control-page-redirection","children":[]},{"level":3,"title":"Auto Locales","slug":"auto-locales","link":"#auto-locales","children":[]},{"level":3,"title":"Automatically switch languages","slug":"automatically-switch-languages","link":"#automatically-switch-languages","children":[]},{"level":3,"title":"Customizing Locale Settings","slug":"customizing-locale-settings","link":"#customizing-locale-settings","children":[]},{"level":3,"title":"Redirecting Sites","slug":"redirecting-sites","link":"#redirecting-sites","children":[]}]},{"level":2,"title":"Plugin Options","slug":"plugin-options","link":"#plugin-options","children":[{"level":3,"title":"config","slug":"config","link":"#config","children":[]},{"level":3,"title":"autoLocale","slug":"autolocale","link":"#autolocale","children":[]},{"level":3,"title":"switchLocale","slug":"switchlocale","link":"#switchlocale","children":[]},{"level":3,"title":"localeConfig","slug":"localeconfig","link":"#localeconfig","children":[]},{"level":3,"title":"localeFallback","slug":"localefallback","link":"#localefallback","children":[]},{"level":3,"title":"defaultBehavior","slug":"defaultbehavior","link":"#defaultbehavior","children":[]},{"level":3,"title":"defaultLocale","slug":"defaultlocale","link":"#defaultlocale","children":[]}]},{"level":2,"title":"Frontmatter options","slug":"frontmatter-options","link":"#frontmatter-options","children":[{"level":3,"title":"redirectFrom","slug":"redirectfrom","link":"#redirectfrom","children":[]},{"level":3,"title":"redirectTo","slug":"redirectto","link":"#redirectto","children":[]}]}],"git":{"updatedTime":1706929170000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/redirect.md"}');export{l as data}; diff --git a/assets/redirect.html-qXZmrThg.js b/assets/redirect.html-qXZmrThg.js new file mode 100644 index 0000000000..7a59c1a939 --- /dev/null +++ b/assets/redirect.html-qXZmrThg.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-eca22bb4","path":"/zh/plugins/redirect.html","title":"redirect","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[{"level":3,"title":"设置重定向","slug":"设置重定向","link":"#设置重定向","children":[]},{"level":3,"title":"自动多语言","slug":"自动多语言","link":"#自动多语言","children":[]},{"level":3,"title":"自动切换语言","slug":"自动切换语言","link":"#自动切换语言","children":[]},{"level":3,"title":"自定义多语言配置","slug":"自定义多语言配置","link":"#自定义多语言配置","children":[]},{"level":3,"title":"重定向站点","slug":"重定向站点","link":"#重定向站点","children":[]}]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"config","slug":"config","link":"#config","children":[]},{"level":3,"title":"autoLocale","slug":"autolocale","link":"#autolocale","children":[]},{"level":3,"title":"switchLocale","slug":"switchlocale","link":"#switchlocale","children":[]},{"level":3,"title":"localeConfig","slug":"localeconfig","link":"#localeconfig","children":[]},{"level":3,"title":"localeFallback","slug":"localefallback","link":"#localefallback","children":[]},{"level":3,"title":"defaultBehavior","slug":"defaultbehavior","link":"#defaultbehavior","children":[]},{"level":3,"title":"defaultLocale","slug":"defaultlocale","link":"#defaultlocale","children":[]}]},{"level":2,"title":"Frontmatter","slug":"frontmatter","link":"#frontmatter","children":[{"level":3,"title":"redirectFrom","slug":"redirectfrom","link":"#redirectfrom","children":[]},{"level":3,"title":"redirectTo","slug":"redirectto","link":"#redirectto","children":[]}]}],"git":{"updatedTime":1706929170000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/redirect.md"}');export{l as data}; diff --git a/assets/redirect.html-zpA2Lu7N.js b/assets/redirect.html-zpA2Lu7N.js new file mode 100644 index 0000000000..32c6a7619c --- /dev/null +++ b/assets/redirect.html-zpA2Lu7N.js @@ -0,0 +1,71 @@ +import{_ as c,r as n,o as i,c as r,a as l,b as s,d as a,w as t,e}from"./app-GUhkEPRO.js";const d={},D=s("h1",{id:"redirect",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#redirect"},[s("span",null,"redirect")])],-1),u=e(`

    此插件提供页面与整站重定向功能。

    使用方法

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

    设置重定向

    如果你改动了已有页面的地址,你可以在 Frontmatter 中使用 redirectFrom 选项设置重定向到此页面的地址,这样可以保证用户在访问旧链接时重定向到新的地址。

    如果你需要将已有的页面重定向到新的页面,可以在 Frontmatter 中使用 redirectTo 选项设置需要重定向到的地址。这样该页面会在访问时重定向到新的地址。

    你还可以通过插件选项中的 config 设置一个重定向映射,详见 config

    自动多语言

    插件可以根据用户的语言首选项,自动将无多语言链接重定向到用户需要的多语言页面。为了实现这一点,你需要留空默认的语言目录 (/),并在插件选项中设置 autoLocale: true。插件会自动根据用户语言跳转到对应的语言页面。

    也就是你需要设置以下目录结构:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    并将主题选项的 locales 设置为:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    这样当用户访问 //page.html 时,他们会自动根据当前浏览器语言重定向到 /en/ /en/page.html/zh/ /zh/page.html

    自定义回退行为

    有些时候,用户可能会在系统设置中添加多个语言。默认情况下,在站点支持首选语言,但首选语言不存在相应页面时,插件会尝试匹配用户设置的备用语言。

    如果不需要回退到用户备用语言,而直接匹配用户首选语言,请在插件选项中设置 localeFallback: false

    自定义缺失行为

    有些时候,当用户访问一个页面时,文档尚未包含用户需要的语言版本 (一个普遍的情况是当前页面尚未完成相关语言的本地化),这样插件需要做出默认行为,你可以通过插件选项中的 defaultBehavior 定制它:

    • "defaultLocale": 重定向到默认语言或首个可用语言页面 (默认行为)
    • "homepage": 重定向到当前语言的主页 (仅在文档包含用户语言时可用)
    • "404": 重定向到当前语言的 404 页 (仅在文档包含用户语言时可用)

    自定义默认路径

    你可以通过设置插件选项中的 defaultLocale 来自定义默认路径。默认情况下,插件会使用 locales 中的第一个键名作为默认路径。

    自动切换语言

    插件支持在多语言文档中,自动根据用户语言首选项,将链接切换到用户需要的多语言页面。为了实现这一点,你需要在插件选项中设置 switchLocale,它可以是以下两个值:

    • direct: 直接切换到用户语言首选项页面,而不询问
    • modal: 在用户语言首选项与当前页面语言不同时,弹出一个对话框询问用户是否切换语言

    自定义多语言配置

    默认情况下,插件会从站点的多语言配置 locales 选项中,读取 语言路径lang 生成多语言配置。有些时候,你可能希望多个语言命中同一个路径,这种情况下,你应该设置插件的 localeConfig 选项。

    比如,你可能希望所有英文用户都匹配到 /en/,并将繁体中文用户匹配到 /zh/ 中,那么你可以设置:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    重定向站点

    有时你可能会更改 base 或为你的站点使用新域名,因此你可能希望原始站点自动重定向到新站点。

    为了解决这个问题,插件提供了 vp-redirect 脚手架。

    使用:
    +  $ vp-redirect generate [源文件夹]
    +
    +Options:
    +  --hostname <hostname>  重定向到的域名 (例如: https://new.example.com/) (默认: /)
    +  -c, --config <config>  设置配置文件路径
    +  -o, --output <output>  设置输出目录 (默认: .vuepress/redirect)
    +  --cache <cache>        设置缓存文件的目录
    +  -t, --temp <temp>      设置临时文件的目录
    +  --clean-cache          生成前清理缓存文件
    +  --clean-temp           生成前清理临时文件
    +  -h, --help             显示此消息
    +

    你需要传入 VuePress 项目源目录并设置 hostname 选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。

    默认情况下,插件将输出到源文件夹下的 .vuepress/redirect 目录。你应该将其上传到你的原始站点以提供重定向。

    选项

    config

    • 类型:Record<string, string> | ((app: App) => Record<string, string>)

    • 详情

      页面重定向映射。

      可直接传入对象或传入参数为 App 的函数返回值一个对象。

      每个键名必须是一个绝对路径,代表重定向的源页面地址。

      每个键值是重定向的目标地址,可以是绝对路径或完整路径。

    • 示例:

      当 base 为 /base/时:

      • /base/foo.html 重定向到 /base/bar.html
      • /base/baz.html 重定向到 https://example.com/qux.html
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      将 post 文件夹的路径重定向到 posts 文件夹

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\\/posts\\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    `,35),v=s("li",null,[a("类型:"),s("code",null,"boolean")],-1),y=s("li",null,[a("默认值: "),s("code",null,"false")],-1),h=s("li",null,"详情: 是否启用语言重定向",-1),m=e('

    switchLocale

    • 类型:"direct" | "modal" | false

    • 默认值: false

    • 详情:

      是否根据用户偏好切换到新的语言环境。

      • "direct": 直接重定向到新的语言环境而不询问
      • "modal": 显示一个模式让用户选择是否切换到新的语言环境

    localeConfig

    • 类型:Record<string, string | string[]>
    • 详情:多语言语言配置

    localeFallback

    • 类型:boolean
    • 默认值: true
    • 详情:是否回退到用户定义的其他语言

    defaultBehavior

    • 类型:"defaultLocale" | "homepage" | "404"
    • 默认值: "defaultLocale"
    • 详情:当前链接没有可用的语言版本时的行为

    defaultLocale

    • 类型:string
    • 默认值: 首个语言路径
    • 详情:默认语言路径

    Frontmatter

    redirectFrom

    • 类型:string | string[]
    • 详情:重定向到该页面的地址。

    redirectTo

    • 类型:string
    • 详情:该页面重定向到的地址。
    ',15);function C(b,E){const p=n("NpmBadge"),o=n("RouterLink");return i(),r("div",null,[D,l(p,{package:"@vuepress/plugin-rtl"}),u,s("ul",null,[v,y,h,s("li",null,[a("参考: "),s("ul",null,[s("li",null,[l(o,{to:"/zh/plugins/guide.html#%E9%87%8D%E5%AE%9A%E5%90%91%E8%AF%AD%E8%A8%80"},{default:t(()=>[a("指南 → 重定向语言")]),_:1})])])])]),m])}const f=c(d,[["render",C],["__file","redirect.html.vue"]]);export{f as default}; diff --git a/assets/register-components.html-H1Om_p-O.js b/assets/register-components.html-H1Om_p-O.js new file mode 100644 index 0000000000..47f4c204bd --- /dev/null +++ b/assets/register-components.html-H1Om_p-O.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3203da3e","path":"/plugins/register-components.html","title":"register-components","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"components","slug":"components","link":"#components","children":[]},{"level":3,"title":"componentsDir","slug":"componentsdir","link":"#componentsdir","children":[]},{"level":3,"title":"componentsPatterns","slug":"componentspatterns","link":"#componentspatterns","children":[]},{"level":3,"title":"getComponentName","slug":"getcomponentname","link":"#getcomponentname","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/register-components.md"}');export{e as data}; diff --git a/assets/register-components.html-K3STLtO8.js b/assets/register-components.html-K3STLtO8.js new file mode 100644 index 0000000000..0f2aa65888 --- /dev/null +++ b/assets/register-components.html-K3STLtO8.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-a51d0de0","path":"/zh/plugins/register-components.html","title":"register-components","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"components","slug":"components","link":"#components","children":[]},{"level":3,"title":"componentsDir","slug":"componentsdir","link":"#componentsdir","children":[]},{"level":3,"title":"componentsPatterns","slug":"componentspatterns","link":"#componentspatterns","children":[]},{"level":3,"title":"getComponentName","slug":"getcomponentname","link":"#getcomponentname","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/register-components.md"}');export{e as data}; diff --git a/assets/register-components.html-hbe_pzQO.js b/assets/register-components.html-hbe_pzQO.js new file mode 100644 index 0000000000..4a8397607b --- /dev/null +++ b/assets/register-components.html-hbe_pzQO.js @@ -0,0 +1,49 @@ +import{_ as c,r as a,o as i,c as r,a as l,b as s,d as n,e}from"./app-GUhkEPRO.js";const t={},D=s("h1",{id:"register-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#register-components"},[s("span",null,"register-components")])],-1),d=e(`

    根据组件文件或目录自动注册 Vue 组件。

    使用方法

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

    配置项

    components

    • 类型: Record<string, string>

    • 默认值: {}

    • 详情:

      一个定义了组件名称和其对应文件路径的对象。

      键会被用作组件名称,值是组件文件的绝对路径。

      如果该配置项中的组件名称和 componentsDir 配置项发生冲突,那么该配置项会有更高的优先级。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • 类型: string | null

    • 默认值: null

    • 详情:

      组件目录的绝对路径。

      该目录下匹配 componentsPatterns 的文件会被自动注册为 Vue 组件。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    组件目录:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    组件会像这样被注册:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    `,16),m=s("li",null,[s("p",null,[n("类型: "),s("code",null,"string[]")])],-1),y=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['**/*.vue']")])],-1),u=s("p",null,"详情:",-1),v={href:"https://github.com/sindresorhus/globby",target:"_blank",rel:"noopener noreferrer"},C=s("p",null,[n("该 Patterns 是相对于 "),s("a",{href:"#componentsdir"},"componentsDir"),n(" 目录的。")],-1),b=e('

    getComponentName

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

    • 默认值: (filename) => path.trimExt(filename.replace(/\\/|\\\\/g, '-'))

    • 详情:

      用于从文件名获取对应组件名称的函数。

      它只会对 componentsDir 目录下匹配了 componentsPatterns 的文件生效。

      注意,这里的 filename 是相对于 componentsPatterns 目录的文件路径。

    ',2);function h(g,E){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return i(),r("div",null,[D,l(p,{package:"@vuepress/plugin-register-components"}),d,s("ul",null,[m,y,s("li",null,[u,s("p",null,[n("使用 "),s("a",v,[n("globby"),l(o)]),n(" 来匹配组件文件的 Patterns 。")]),C])]),b])}const f=c(t,[["render",h],["__file","register-components.html.vue"]]);export{f as default}; diff --git a/assets/register-components.html-kvj0B1Hx.js b/assets/register-components.html-kvj0B1Hx.js new file mode 100644 index 0000000000..67ffdc8e23 --- /dev/null +++ b/assets/register-components.html-kvj0B1Hx.js @@ -0,0 +1,49 @@ +import{_ as t,r as a,o as i,c,a as e,b as s,d as n,e as l}from"./app-GUhkEPRO.js";const r={},D=s("h1",{id:"register-components",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#register-components"},[s("span",null,"register-components")])],-1),d=l(`

    Register Vue components from component files or directory automatically.

    Usage

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

    Options

    components

    • Type: Record<string, string>

    • Default: {}

    • Details:

      An object that defines name of components and their corresponding file path.

      The key will be used as the component name, and the value is an absolute path of the component file.

      If the component name from this option conflicts with componentsDir option, this option will have a higher priority.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • Type: string | null

    • Default: null

    • Details:

      Absolute path to the components directory.

      Files in this directory which are matched with componentsPatterns will be registered as Vue components automatically.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    Components directory:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    Components will be registered like this:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    `,16),m=s("li",null,[s("p",null,[n("Type: "),s("code",null,"string[]")])],-1),y=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['**/*.vue']")])],-1),u=s("p",null,"Details:",-1),v={href:"https://github.com/sindresorhus/globby",target:"_blank",rel:"noopener noreferrer"},C=s("p",null,[n("The patterns are relative to "),s("a",{href:"#componentsdir"},"componentsDir"),n(".")],-1),h=l('

    getComponentName

    • Type: (filename: string) => string

    • Default: (filename) => path.trimExt(filename.replace(/\\/|\\\\/g, '-'))

    • Details:

      A function to get component name from the filename.

      It will only take effect on the files in the componentsDir which are matched with the componentsPatterns.

      Notice that the filename is a filepath relative to componentsDir.

    ',2);function b(g,E){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return i(),c("div",null,[D,e(p,{package:"@vuepress/plugin-register-components"}),d,s("ul",null,[m,y,s("li",null,[u,s("p",null,[n("Patterns to match component files using "),s("a",v,[n("globby"),e(o)]),n(".")]),C])]),h])}const _=t(r,[["render",b],["__file","register-components.html.vue"]]);export{_ as default}; diff --git a/assets/remove-pwa.html-TF0wHWZS.js b/assets/remove-pwa.html-TF0wHWZS.js new file mode 100644 index 0000000000..2dd3f98b81 --- /dev/null +++ b/assets/remove-pwa.html-TF0wHWZS.js @@ -0,0 +1,11 @@ +import{_ as i,r as s,o as r,c as t,a,b as e,d as n,w as c,e as p}from"./app-GUhkEPRO.js";const d={},u=e("h1",{id:"remove-pwa",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#remove-pwa"},[e("span",null,"remove-pwa")])],-1),h=e("p",null,"This plugin removes any related service worker from your VuePress site, so that users can still get updates if you removed any PWA plugin after enabling it.",-1),v={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"Why this plugin is needed if you used PWA plugin once?",-1),D=e("code",null,"@vuepress/plugin-pwa",-1),w=e("p",null,"However, if you remove pwa plugin, the old service worker will still be there, but they can never get an update because they can never found a new service worker to update to. So users will stay with the old version of your site.",-1),g=e("p",null,"To solve this problem:",-1),y=e("ol",null,[e("li",null,"A new service worker with empty contents shall be generated in the original place."),e("li",null,"The new service worker shall attempt to remove contents that old service worker cached, then it should unregister itself.")],-1),f=p(`

    Usage

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

    Config

    cachePrefix

    • Type: string
    • Default: 'workbox'
    • Details: The cache prefix for the service worker.

    swLocation

    • Type: string
    • Default: 'service-worker.js'
    • Details: The location of the old service worker.
    `,8);function _(b,k){const l=s("NpmBadge"),o=s("RouterLink");return r(),t("div",null,[u,a(l,{package:"@vuepress/plugin-remove-pwa"}),h,e("div",v,[m,e("p",null,[n("PWA plugins like "),a(o,{to:"/plugins/pwa.html"},{default:c(()=>[D]),_:1}),n(" register service worker to your site, which will cache your site and make it available offline.")]),w,g,y]),f])}const C=i(d,[["render",_],["__file","remove-pwa.html.vue"]]);export{C as default}; diff --git a/assets/remove-pwa.html-ptxYFHYI.js b/assets/remove-pwa.html-ptxYFHYI.js new file mode 100644 index 0000000000..7232b088e5 --- /dev/null +++ b/assets/remove-pwa.html-ptxYFHYI.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-cfa3cd00","path":"/plugins/remove-pwa.html","title":"remove-pwa","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Config","slug":"config","link":"#config","children":[{"level":3,"title":"cachePrefix","slug":"cacheprefix","link":"#cacheprefix","children":[]},{"level":3,"title":"swLocation","slug":"swlocation","link":"#swlocation","children":[]}]}],"git":{"updatedTime":1706931533000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/remove-pwa.md"}');export{e as data}; diff --git a/assets/remove-pwa.html-whrdIVXP.js b/assets/remove-pwa.html-whrdIVXP.js new file mode 100644 index 0000000000..64889e8993 --- /dev/null +++ b/assets/remove-pwa.html-whrdIVXP.js @@ -0,0 +1,11 @@ +import{_ as i,r as s,o as r,c,a,b as e,d as n,w as p,e as t}from"./app-GUhkEPRO.js";const d={},u=e("h1",{id:"remove-pwa",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#remove-pwa"},[e("span",null,"remove-pwa")])],-1),v=e("p",null,"此插件从你的 VuePress 站点中删除任何相关的 Service Worker,因此如果你在启用后任何 PWA 插件后移除它们,用户仍然可以获得更新。",-1),h={class:"custom-container tip"},m=e("p",{class:"custom-container-title"},"如果你启用过 PWA,为什么需要这个插件?",-1),D=e("code",null,"@vuepress/plugin-pwa",-1),_=e("p",null,"但是,如果你删除 PWA 插件,先前的 Service Worker 仍将在那里,但它们永远无法获得更新,因为他们永远无法找到要更新的新 Service Worker。 因此,用户将继续使用你网站的旧版本。",-1),b=e("p",null,"要解决这个问题:",-1),g=e("ol",null,[e("li",null,[e("p",null,"一个新的内容为空的 Service Worker 需要生成在原位置。")]),e("li",null,[e("p",null,"新的 Service Worker 应该尝试删除旧 Service Worker 缓存的内容,然后它应该注销自己。")])],-1),k=t(`

    Usage

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

    Config

    cachePrefix

    • 类型:string
    • 默认值:'workbox'
    • 详情:Service worker 的缓存前缀。

    swLocation

    • 类型: string
    • 默认值:'service-worker.js'
    • 详情:旧 Service Worker 的位置。
    `,8);function y(f,C){const l=s("NpmBadge"),o=s("RouterLink");return r(),c("div",null,[u,a(l,{package:"@vuepress/plugin-remove-pwa"}),v,e("div",h,[m,e("p",null,[n("PWA 插件,如 "),a(o,{to:"/zh/plugins/pwa.html"},{default:p(()=>[D]),_:1}),n(" 将 Service Worker 注册到你的站点,这将缓存你的站点并使其离线可用。")]),_,b,g]),k])}const x=i(d,[["render",y],["__file","remove-pwa.html.vue"]]);export{x as default}; diff --git a/assets/remove-pwa.html-zBr2B-69.js b/assets/remove-pwa.html-zBr2B-69.js new file mode 100644 index 0000000000..767a58b1f6 --- /dev/null +++ b/assets/remove-pwa.html-zBr2B-69.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-444d9b1e","path":"/zh/plugins/remove-pwa.html","title":"remove-pwa","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Config","slug":"config","link":"#config","children":[{"level":3,"title":"cachePrefix","slug":"cacheprefix","link":"#cacheprefix","children":[]},{"level":3,"title":"swLocation","slug":"swlocation","link":"#swlocation","children":[]}]}],"git":{"updatedTime":1706931533000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/remove-pwa.md"}');export{e as data}; diff --git a/assets/rtl.html-5KfB6Rkt.js b/assets/rtl.html-5KfB6Rkt.js new file mode 100644 index 0000000000..ce5df7384a --- /dev/null +++ b/assets/rtl.html-5KfB6Rkt.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-0503d737","path":"/plugins/rtl.html","title":"rtl","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]}]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/rtl.md"}');export{e as data}; diff --git a/assets/rtl.html-IhpUR5z5.js b/assets/rtl.html-IhpUR5z5.js new file mode 100644 index 0000000000..7449ccb709 --- /dev/null +++ b/assets/rtl.html-IhpUR5z5.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-bfe10934","path":"/zh/plugins/rtl.html","title":"rtl","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"选项","slug":"选项","link":"#选项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"selector","slug":"selector","link":"#selector","children":[]}]}],"git":{"updatedTime":1706801200000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/rtl.md"}');export{l as data}; diff --git a/assets/rtl.html-S66r9gJG.js b/assets/rtl.html-S66r9gJG.js new file mode 100644 index 0000000000..51b0bb0828 --- /dev/null +++ b/assets/rtl.html-S66r9gJG.js @@ -0,0 +1,17 @@ +import{_ as n,r as l,o as e,c as p,a as o,b as s,e as i}from"./app-GUhkEPRO.js";const t={},c=s("h1",{id:"rtl",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#rtl"},[s("span",null,"rtl")])],-1),r=i(`

    This plugin will set direction to rtl on configured locales.

    Usage

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

    Options

    locales

    • Type: string[]
    • Default: ['/']
    • Details: Locale path to enable rtl.

    selector

    • Type: SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • Default: { 'html': { dir: 'rtl' } }

    • Details:

      Selector to enable rtl.

      The default settings mean that the dir attribute of the html element will be set to rtl in rtl locales.

    `,9);function d(D,u){const a=l("NpmBadge");return e(),p("div",null,[c,o(a,{package:"@vuepress/plugin-rtl"}),r])}const h=n(t,[["render",d],["__file","rtl.html.vue"]]);export{h as default}; diff --git a/assets/rtl.html-lfnNHMRj.js b/assets/rtl.html-lfnNHMRj.js new file mode 100644 index 0000000000..216efbce66 --- /dev/null +++ b/assets/rtl.html-lfnNHMRj.js @@ -0,0 +1,17 @@ +import{_ as n,r as l,o as e,c as p,a as o,b as s,e as c}from"./app-GUhkEPRO.js";const i={},r=s("h1",{id:"rtl",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#rtl"},[s("span",null,"rtl")])],-1),t=c(`

    此插件会在配置的语言上设置 rtl 方向。

    使用方法

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

    选项

    locales

    • 类型:string[]
    • 默认值:['/']
    • 详情: 开启 RTL 布局的多语言路径。

    selector

    • 类型:SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • 默认值:{ 'html': { dir: 'rtl' } }

    • 详情:

      开启 RTL 的选择器。

      默认设置意味着在 RTL 多语言中,html 元素的 dir 属性将被设置为 rtl

    `,9);function d(D,u){const a=l("NpmBadge");return e(),p("div",null,[r,o(a,{package:"@vuepress/plugin-rtl"}),t])}const y=n(i,[["render",d],["__file","rtl.html.vue"]]);export{y as default}; diff --git a/assets/search.html-3pAnfhg9.js b/assets/search.html-3pAnfhg9.js new file mode 100644 index 0000000000..eca3de1110 --- /dev/null +++ b/assets/search.html-3pAnfhg9.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-72a3dbcc","path":"/zh/plugins/search.html","title":"search","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"本地搜索索引","slug":"本地搜索索引","link":"#本地搜索索引","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"hotKeys","slug":"hotkeys","link":"#hotkeys","children":[]},{"level":3,"title":"maxSuggestions","slug":"maxsuggestions","link":"#maxsuggestions","children":[]},{"level":3,"title":"isSearchable","slug":"issearchable","link":"#issearchable","children":[]},{"level":3,"title":"getExtraFields","slug":"getextrafields","link":"#getextrafields","children":[]}]},{"level":2,"title":"样式","slug":"样式","link":"#样式","children":[]},{"level":2,"title":"组件","slug":"组件","link":"#组件","children":[{"level":3,"title":"SearchBox","slug":"searchbox","link":"#searchbox","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/search.md"}');export{e as data}; diff --git a/assets/search.html-YtY13_8l.js b/assets/search.html-YtY13_8l.js new file mode 100644 index 0000000000..e8b55f7dee --- /dev/null +++ b/assets/search.html-YtY13_8l.js @@ -0,0 +1,75 @@ +import{_ as r,r as e,o as t,c as d,a,b as s,d as n,w as o,e as l}from"./app-GUhkEPRO.js";const D={},y=s("h1",{id:"search",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#search"},[s("span",null,"search")])],-1),u=l(`

    为你的文档网站提供本地搜索能力。

    提示

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

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

    使用方法

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

    本地搜索索引

    该插件会根据你的页面,在本地生成搜索索引,然后在用户访问站点时加载搜索索引文件。换句话说,这是一个轻量级的内置搜索能力,不会进行任何外部请求。

    `,7),v=l(`

    配置项

    locales

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

    • 详情:

      搜索框在不同 locales 下的文字。

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

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,4),h=l(`

    hotKeys

    • 类型: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of \`event.key\` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press \`event.altKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press \`event.ctrlKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press \`event.shiftKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    `,3),C=s("li",null,[s("p",null,[n("默认值: "),s("code",null,"['s', '/']")])],-1),m=s("p",null,"详情:",-1),b={href:"http://keycode.info/",target:"_blank",rel:"noopener noreferrer"},E=s("p",null,"当按下热键时,搜索框会被聚焦。",-1),g=s("p",null,"将该配置项设为空数组可以禁用热键功能。",-1),f=l(`

    maxSuggestions

    • 类型: number

    • 默认值: 5

    • 详情:

      指定搜索结果的最大条数。

    isSearchable

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

    • 默认值: () => true

    • 详情:

      一个函数,用于判断一个页面是否应该被包含在搜索索引中。

      • 返回 true 来包含该页面。
      • 返回 false 来排除该页面。
    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 排除首页
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

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

    • 默认值: () => []

    • 详情:

      一个函数,用于在页面的搜索索引中添加额外字段。

      默认情况下,该插件会将页面标题和小标题作为搜索索引。该配置项可以帮助你添加更多的可搜索字段。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 允许搜索 Frontmatter 中的 \`tags\`
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义搜索框的样式:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    组件

    • 详情:

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

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

    提示

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

    `,15);function x(_,A){const c=e("NpmBadge"),p=e("RouterLink"),i=e("ExternalLinkIcon");return t(),d("div",null,[y,a(c,{package:"@vuepress/plugin-search"}),u,s("p",null,[n("然而,当你的站点包含大量页面时,搜索索引文件也会变得非常大,它可能会拖慢你的页面加载速度。在这种情况下,我们建议你使用更成熟的解决方案 - "),a(p,{to:"/zh/plugins/docsearch.html"},{default:o(()=>[n("docsearch")]),_:1}),n(" 。")]),v,s("ul",null,[s("li",null,[n("参考: "),s("ul",null,[s("li",null,[a(p,{to:"/guide/i18n.html"},{default:o(()=>[n("指南 > 多语言支持")]),_:1})])])])]),h,s("ul",null,[C,s("li",null,[m,s("p",null,[n("指定热键的 "),s("a",b,[n("event.key"),a(i)]),n(" 。")]),E,g])]),f])}const k=r(D,[["render",x],["__file","search.html.vue"]]);export{k as default}; diff --git a/assets/search.html-gMcjE234.js b/assets/search.html-gMcjE234.js new file mode 100644 index 0000000000..c8d5fcc912 --- /dev/null +++ b/assets/search.html-gMcjE234.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-12124eae","path":"/plugins/search.html","title":"search","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Local Search Index","slug":"local-search-index","link":"#local-search-index","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"locales","slug":"locales","link":"#locales","children":[]},{"level":3,"title":"hotKeys","slug":"hotkeys","link":"#hotkeys","children":[]},{"level":3,"title":"maxSuggestions","slug":"maxsuggestions","link":"#maxsuggestions","children":[]},{"level":3,"title":"isSearchable","slug":"issearchable","link":"#issearchable","children":[]},{"level":3,"title":"getExtraFields","slug":"getextrafields","link":"#getextrafields","children":[]}]},{"level":2,"title":"Styles","slug":"styles","link":"#styles","children":[]},{"level":2,"title":"Components","slug":"components","link":"#components","children":[{"level":3,"title":"SearchBox","slug":"searchbox","link":"#searchbox","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/search.md"}');export{e as data}; diff --git a/assets/search.html-jkn-b6oZ.js b/assets/search.html-jkn-b6oZ.js new file mode 100644 index 0000000000..e65a2920b5 --- /dev/null +++ b/assets/search.html-jkn-b6oZ.js @@ -0,0 +1,75 @@ +import{_ as t,r as l,o as r,c as d,a,b as s,d as n,w as o,e}from"./app-GUhkEPRO.js";const D={},u=s("h1",{id:"search",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#search"},[s("span",null,"search")])],-1),y=e(`

    Provide local search to your documentation site.

    TIP

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

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

    Usage

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

    Local Search Index

    This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests.

    `,7),h=e(`

    Options

    locales

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

    • Details:

      The text of the search box in different locales.

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

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +
    `,4),v=e(`

    hotKeys

    • Type: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of \`event.key\` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press \`event.altKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press \`event.ctrlKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press \`event.shiftKey\` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    `,3),m=s("li",null,[s("p",null,[n("Default: "),s("code",null,"['s', '/']")])],-1),b=s("p",null,"Details:",-1),C={href:"http://keycode.info/",target:"_blank",rel:"noopener noreferrer"},g=s("p",null,"When hotkeys are pressed, the search box input will be focused.",-1),f=s("p",null,"Set to an empty array to disable hotkeys.",-1),E=e(`

    maxSuggestions

    • Type: number

    • Default: 5

    • Details:

      Specify the maximum number of search results.

    isSearchable

    • Type: (page: Page) => boolean

    • Default: () => true

    • Details:

      A function to determine whether a page should be included in the search index.

      • Return true to include the page.
      • Return false to exclude the page.
    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // exclude the homepage
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

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

    • Default: () => []

    • Details:

      A function to add extra fields to the search index of a page.

      By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields.

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // allow searching the \`tags\` frontmatter
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    Styles

    You can customize the style of the search box via CSS variables:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    Components

    • Details:

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

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

    TIP

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

    `,15);function x(A,_){const i=l("NpmBadge"),p=l("RouterLink"),c=l("ExternalLinkIcon");return r(),d("div",null,[u,a(i,{package:"@vuepress/plugin-search"}),y,s("p",null,[n("However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - "),a(p,{to:"/plugins/docsearch.html"},{default:o(()=>[n("docsearch")]),_:1}),n(".")]),h,s("ul",null,[s("li",null,[n("Also see: "),s("ul",null,[s("li",null,[a(p,{to:"/guide/i18n.html"},{default:o(()=>[n("Guide > I18n")]),_:1})])])])]),v,s("ul",null,[m,s("li",null,[b,s("p",null,[n("Specify the "),s("a",C,[n("event.key"),a(c)]),n(" of the hotkeys.")]),g,f])]),E])}const F=t(D,[["render",x],["__file","search.html.vue"]]);export{F as default}; diff --git a/assets/shiki.html-b2u78EYO.js b/assets/shiki.html-b2u78EYO.js new file mode 100644 index 0000000000..c5e73bbacd --- /dev/null +++ b/assets/shiki.html-b2u78EYO.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-35900b8f","path":"/plugins/shiki.html","title":"shiki","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"langs","slug":"langs","link":"#langs","children":[]},{"level":3,"title":"theme","slug":"theme","link":"#theme","children":[]},{"level":3,"title":"themes","slug":"themes","link":"#themes","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/shiki.md"}');export{e as data}; diff --git a/assets/shiki.html-bqkMqjP9.js b/assets/shiki.html-bqkMqjP9.js new file mode 100644 index 0000000000..8bf066ffe8 --- /dev/null +++ b/assets/shiki.html-bqkMqjP9.js @@ -0,0 +1,12 @@ +import{_ as t,r as a,o as p,c as r,a as e,b as s,d as n,e as i}from"./app-GUhkEPRO.js";const c={},d=s("h1",{id:"shiki",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#shiki"},[s("span",null,"shiki")])],-1),h={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://shikiji.netlify.app/",target:"_blank",rel:"noopener noreferrer"},_={class:"custom-container tip"},D=s("p",{class:"custom-container-title"},"提示",-1),m={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},k={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},y=s("p",null,[n("你可以考虑在 "),s("code",null,"dev"),n(" 模式下禁用该插件来获取更好的开发体验。")],-1),v=i(`

    使用方法

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // 配置项
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    配置项

    langs

    `,5),g=s("li",null,[s("p",null,[n("类型: "),s("code",null,"ShikiLang[]")])],-1),b=s("li",null,[s("p",null,"详情:"),s("p",null,"Shikiji 要解析的代码块的语言。"),s("p",null,[n("该配置项会被传递到 Shikiji 的 "),s("code",null,"getHighlighter()"),n(" 方法中。")]),s("p",null,"你需要明确传入所有你使用的语言列表,否则 Shikiji 将不会加载任何语言。")],-1),f=s("p",null,"参考:",-1),C={href:"https://shikiji.netlify.app/languages",target:"_blank",rel:"noopener noreferrer"},E=s("h3",{id:"theme",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme"},[s("span",null,"theme")])],-1),j=i("
  • 类型: ShikiTheme

  • 默认值: 'nord'

  • 详情:

    Shikiji 的主题。

    该配置项会被传递到 Shikiji 的 codeToHtml() 方法中。

  • ",3),x=s("p",null,"参考:",-1),S={href:"https://shikiji.netlify.app/themes",target:"_blank",rel:"noopener noreferrer"},T=s("h3",{id:"themes",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#themes"},[s("span",null,"themes")])],-1),N=s("li",null,[s("p",null,[n("类型:"),s("code",null,"Record<'dark' | 'light', ShikiTheme>")])],-1),A=s("li",null,[s("p",null,"详情:"),s("p",null,"Shikiji 的暗黑和明亮模式双主题。"),s("p",null,[n("该配置项会被传递到 Shikiji 的 "),s("code",null,"codeToHtml()"),n(" 方法中。")])],-1),B=s("p",null,"参考:",-1),V={href:"https://shikiji.netlify.app/guide/dual-themes",target:"_blank",rel:"noopener noreferrer"};function L(F,H){const o=a("NpmBadge"),l=a("ExternalLinkIcon");return p(),r("div",null,[d,e(o,{package:"@vuepress/plugin-shiki"}),s("p",null,[n("该插件使用 "),s("a",h,[n("Shiki"),e(l)]),n(" ("),s("a",u,[n("Shikiji"),e(l)]),n(") 来为 Markdown 代码块启用代码高亮。")]),s("div",_,[D,s("p",null,[s("a",m,[n("Shiki"),e(l)]),n(" 是 VSCode 正在使用的代码高亮器。它具有更高的保真度,但可能会比 "),s("a",k,[n("Prism.js"),e(l)]),n(" 要慢一些,特别是在有大量代码块需要处理的时候。")]),y]),v,s("ul",null,[g,b,s("li",null,[f,s("ul",null,[s("li",null,[s("a",C,[n("shikiji > Languages"),e(l)])])])])]),E,s("ul",null,[j,s("li",null,[x,s("ul",null,[s("li",null,[s("a",S,[n("shikiji > Themes"),e(l)])])])])]),T,s("ul",null,[N,A,s("li",null,[B,s("ul",null,[s("li",null,[s("a",V,[n("shikiji > Dual Themes"),e(l)])])])])])])}const I=t(c,[["render",L],["__file","shiki.html.vue"]]);export{I as default}; diff --git a/assets/shiki.html-eykzOzfX.js b/assets/shiki.html-eykzOzfX.js new file mode 100644 index 0000000000..317e6ce23c --- /dev/null +++ b/assets/shiki.html-eykzOzfX.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3c435ffe","path":"/zh/plugins/shiki.html","title":"shiki","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"langs","slug":"langs","link":"#langs","children":[]},{"level":3,"title":"theme","slug":"theme","link":"#theme","children":[]},{"level":3,"title":"themes","slug":"themes","link":"#themes","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/shiki.md"}');export{e as data}; diff --git a/assets/shiki.html-z5Ugr0Bc.js b/assets/shiki.html-z5Ugr0Bc.js new file mode 100644 index 0000000000..ae50fa6df1 --- /dev/null +++ b/assets/shiki.html-z5Ugr0Bc.js @@ -0,0 +1,12 @@ +import{_ as t,r as a,o as p,c as r,a as n,b as s,d as e,e as i}from"./app-GUhkEPRO.js";const c={},d=s("h1",{id:"shiki",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#shiki"},[s("span",null,"shiki")])],-1),h={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://shikiji.netlify.app/",target:"_blank",rel:"noopener noreferrer"},_={class:"custom-container tip"},D=s("p",{class:"custom-container-title"},"TIP",-1),m={href:"https://shiki.matsu.io/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://prismjs.com/",target:"_blank",rel:"noopener noreferrer"},k=s("p",null,[e("You could consider disabling this plugin in "),s("code",null,"dev"),e(" mode to get better development experience.")],-1),y=i(`

    Usage

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // options
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    Options

    langs

    `,5),b=s("li",null,[s("p",null,[e("Type: "),s("code",null,"ShikiLang[]")])],-1),f=s("li",null,[s("p",null,"Details:"),s("p",null,"Languages of code blocks to be parsed by shikiji."),s("p",null,[e("This option will be forwarded to "),s("code",null,"getHighlighter()"),e(" method of shikiji.")]),s("p",null,"You need to provide the languages list you are using explicitly, otherwise shikiji won't load any languages.")],-1),v=s("p",null,"Also see:",-1),C={href:"https://shikiji.netlify.app/languages",target:"_blank",rel:"noopener noreferrer"},x=s("h3",{id:"theme",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme"},[s("span",null,"theme")])],-1),E=i("
  • Type: ShikiTheme

  • Default: 'nord'

  • Details:

    Theme of shikiji.

    This option will be forwarded to codeToHtml() method of shikiji.

  • ",3),j=s("p",null,"Also see:",-1),T={href:"https://shikiji.netlify.app/themes",target:"_blank",rel:"noopener noreferrer"},w=s("h3",{id:"themes",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#themes"},[s("span",null,"themes")])],-1),A=s("li",null,[s("p",null,[e("Type: "),s("code",null,"Record<'dark' | 'light', ShikiTheme>")])],-1),S=s("li",null,[s("p",null,"Details:"),s("p",null,"Dark / Light Dual themes of shikiji."),s("p",null,[e("This option will be forwarded to "),s("code",null,"codeToHtml()"),e(" method of shikiji.")])],-1),L=s("p",null,"Also see:",-1),N={href:"https://shikiji.netlify.app/guide/dual-themes",target:"_blank",rel:"noopener noreferrer"};function B(V,I){const o=a("NpmBadge"),l=a("ExternalLinkIcon");return p(),r("div",null,[d,n(o,{package:"@vuepress/plugin-shiki"}),s("p",null,[e("This plugin will enable syntax highlighting for markdown code fence with "),s("a",h,[e("Shiki"),n(l)]),e(" ("),s("a",u,[e("Shikiji"),n(l)]),e(").")]),s("div",_,[D,s("p",null,[s("a",m,[e("Shiki"),n(l)]),e(" is the syntax highlighter being used by VSCode. It has higher fidelity, but it could be slower than "),s("a",g,[e("Prism.js"),n(l)]),e(", especially when you have a lot of code blocks.")]),k]),y,s("ul",null,[b,f,s("li",null,[v,s("ul",null,[s("li",null,[s("a",C,[e("shikiji > Languages"),n(l)])])])])]),x,s("ul",null,[E,s("li",null,[j,s("ul",null,[s("li",null,[s("a",T,[e("shikiji > Themes"),n(l)])])])])]),w,s("ul",null,[A,S,s("li",null,[L,s("ul",null,[s("li",null,[s("a",N,[e("shikiji > Dual Themes"),n(l)])])])])])])}const F=t(c,[["render",B],["__file","shiki.html.vue"]]);export{F as default}; diff --git a/assets/style-0tJNvPcn.css b/assets/style-0tJNvPcn.css new file mode 100644 index 0000000000..d5c46cc721 --- /dev/null +++ b/assets/style-0tJNvPcn.css @@ -0,0 +1 @@ +.vp-back-to-top-button{position:fixed!important;bottom:4rem;inset-inline-end:1rem;z-index:100;width:3rem;height:3rem;padding:.5rem;border-width:0;border-radius:50%;background:var(--back-to-top-bg-color);color:var(--back-to-top-color);box-shadow:2px 2px 10px 4px var(--back-to-top-shadow);cursor:pointer}@media (max-width: 959px){.vp-back-to-top-button{width:2.5rem;height:2.5rem}}@media print{.vp-back-to-top-button{display:none}}.vp-back-to-top-button:hover{color:var(--back-to-top-color-hover)}.vp-back-to-top-button .back-to-top-icon{overflow:hidden;width:100%;height:100%;background:currentcolor;border-radius:50%;-webkit-mask-image:var(--back-to-top-icon);mask-image:var(--back-to-top-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:cover;mask-size:cover}.vp-scroll-progress{position:absolute;right:-2px;bottom:-2px;width:calc(100% + 4px);height:calc(100% + 4px)}.vp-scroll-progress svg{width:100%;height:100%}.vp-scroll-progress circle{opacity:.9;fill:none;stroke:currentColor;transform:rotate(-90deg);transform-origin:50% 50%;r:22;stroke-dasharray:0% 314.1593%;stroke-width:3px}@media (max-width: 959px){.vp-scroll-progress circle{r:18}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--back-to-top-z-index: 5;--back-to-top-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%201024%201024'%3e%3cpath%20d='M512%20843.2c-36.2%200-66.4-13.6-85.8-21.8-10.8-4.6-22.6%203.6-21.8%2015.2l7%20102c.4%206.2%207.6%209.4%2012.6%205.6l29-22c3.6-2.8%209-1.8%2011.4%202l41%2064.2c3%204.8%2010.2%204.8%2013.2%200l41-64.2c2.4-3.8%207.8-4.8%2011.4-2l29%2022c5%203.8%2012.2.6%2012.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6%208.2-49.6%2021.8-85.8%2021.8'/%3e%3cpath%20d='m795.4%20586.2-96-98.2C699.4%20172%20513%2032%20513%2032S324.8%20172%20324.8%20488l-96%2098.2c-3.6%203.6-5.2%209-4.4%2014.2L261.2%20824c1.8%2011.4%2014.2%2017%2023.6%2010.8L419%20744s41.4%2040%2094.2%2040%2092.2-40%2092.2-40l134.2%2090.8c9.2%206.2%2021.6.6%2023.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14M513%20384c-34%200-61.4-28.6-61.4-64s27.6-64%2061.4-64c34%200%2061.4%2028.6%2061.4%2064S547%20384%20513%20384'/%3e%3c/svg%3e");--back-to-top-bg-color: #fff;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3;--back-to-top-shadow: rgb(0 0 0 / 20%)}div[class*=language-]:hover:before{display:none}div[class*=language-]:hover .vp-copy-code-button{opacity:1}.vp-copy-code-button{position:absolute;top:.5em;right:.5em;z-index:5;width:2.5rem;height:2.5rem;padding:0;border-width:0;border-radius:.5rem;background:transparent;outline:none;opacity:0;cursor:pointer;transition:opacity .4s}@media print{.vp-copy-code-button{display:none}}.vp-copy-code-button:focus,.vp-copy-code-button.copied{opacity:1}.vp-copy-code-button:hover,.vp-copy-code-button.copied{background:var(--copy-code-hover)}.vp-copy-code-button.copied .vp-copy-icon{-webkit-mask-image:var(--code-copied-icon);mask-image:var(--code-copied-icon)}.vp-copy-code-button.copied:after{content:attr(data-copied);position:absolute;top:0;right:calc(100% + .25rem);display:block;height:1.25rem;padding:.625rem;border-radius:.5rem;background:var(--copy-code-hover);color:var(--copy-code-color);font-weight:500;line-height:1.25rem;white-space:nowrap}.vp-copy-icon{width:1.25rem;height:1.25rem;padding:.625rem;background:currentcolor;color:var(--copy-code-color);font-size:1.25rem;-webkit-mask-image:var(--code-copy-icon);mask-image:var(--code-copy-icon);-webkit-mask-position:50%;mask-position:50%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:1em;mask-size:1em}:root{--code-copy-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202'%20/%3e%3c/svg%3e");--code-copied-icon: url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20fill='none'%20height='20'%20width='20'%20stroke='rgba(128,128,128,1)'%20stroke-width='2'%3e%3cpath%20stroke-linecap='round'%20stroke-linejoin='round'%20d='M9%205H7a2%202%200%200%200-2%202v12a2%202%200%200%200%202%202h10a2%202%200%200%200%202-2V7a2%202%200%200%200-2-2h-2M9%205a2%202%200%200%200%202%202h2a2%202%200%200%200%202-2M9%205a2%202%200%200%201%202-2h2a2%202%200%200%201%202%202m-6%209%202%202%204-4'%20/%3e%3c/svg%3e");--copy-code-color: var(--code-ln-color, #9e9e9e);--copy-code-hover: var(--code-hl-bg-color, rgb(0 0 0 / 66%))}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--c-code-group-tab-title: rgba(255, 255, 255, .9);--c-code-group-tab-bg: var(--code-bg-color);--c-code-group-tab-outline: var(var(--c-code-group-tab-title));--c-code-group-tab-active-border: var(--c-brand);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.back-to-top{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light);--back-to-top-bg-color: var(--c-bg)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}.pwa-popup{--pwa-popup-text-color: var(--c-text);--pwa-popup-bg-color: var(--c-bg);--pwa-popup-border-color: var(--c-brand);--pwa-popup-shadow: 0 4px 16px var(--c-brand);--pwa-popup-btn-text-color: var(--c-bg);--pwa-popup-btn-bg-color: var(--c-brand);--pwa-popup-btn-hover-bg-color: var(--c-brand-light)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1 .header-anchor,h2 .header-anchor,h3 .header-anchor,h4 .header-anchor,h5 .header-anchor,h6 .header-anchor{color:inherit;text-decoration:none;position:relative}h1 .header-anchor:hover:before,h2 .header-anchor:hover:before,h3 .header-anchor:hover:before,h4 .header-anchor:hover:before,h5 .header-anchor:hover:before,h6 .header-anchor:hover:before{font-size:.8em;content:"¶";position:absolute;left:-.75em;color:var(--c-brand)}h1 .header-anchor:focus-visible,h2 .header-anchor:focus-visible,h3 .header-anchor:focus-visible,h4 .header-anchor:focus-visible,h5 .header-anchor:focus-visible,h6 .header-anchor:focus-visible{outline:none}h1 .header-anchor:focus-visible:before,h2 .header-anchor:focus-visible:before,h3 .header-anchor:focus-visible:before,h4 .header-anchor:focus-visible:before,h5 .header-anchor:focus-visible:before,h6 .header-anchor:focus-visible:before{content:"¶";position:absolute;left:-.75em;color:var(--c-brand);outline:auto}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid var(--c-bg-arrow)}.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid var(--c-bg-arrow)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-title);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--c-code-group-tab-bg)}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:var(--c-code-group-tab-title);font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid var(--c-code-group-tab-outline)}.code-group__nav-tab-active{border-bottom:var(--c-code-group-tab-active-border) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:not(.header-anchor):hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.router-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.router-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.router-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.page-meta{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem;overflow:auto}@media (max-width: 959px){.page-meta{padding:2rem}}@media (max-width: 419px){.page-meta{padding:1.5rem}}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}@media print{.page-meta .edit-link{display:none}}.page-meta .last-updated{float:right}@media (max-width: 719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem 2rem;padding-bottom:0}@media (max-width: 959px){.page-nav{padding:2rem}}@media (max-width: 419px){.page-nav{padding:1.5rem}}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .prev a:before{content:"←"}.page-nav .next{float:right}.page-nav .next a:after{content:"→"}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}.npm-badge[data-v-c758b2a0]{margin-right:.5rem} diff --git a/assets/styles.html-6NsWkdvc.js b/assets/styles.html-6NsWkdvc.js new file mode 100644 index 0000000000..8a8c0c27c1 --- /dev/null +++ b/assets/styles.html-6NsWkdvc.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-392d5b46","path":"/themes/default/styles.html","title":"Styles","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Palette File","slug":"palette-file","link":"#palette-file","children":[]},{"level":2,"title":"Style File","slug":"style-file","link":"#style-file","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"themes/default/styles.md"}');export{e as data}; diff --git a/assets/styles.html-MtQFW6Eq.js b/assets/styles.html-MtQFW6Eq.js new file mode 100644 index 0000000000..837d7f0a06 --- /dev/null +++ b/assets/styles.html-MtQFW6Eq.js @@ -0,0 +1,232 @@ +import{_ as e,r as a,o as c,c as D,a as l,b as s,d as n,e as r}from"./app-GUhkEPRO.js";const t={},i=s("h1",{id:"样式",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#样式"},[s("span",null,"样式")])],-1),y={href:"https://sass-lang.com/",target:"_blank",rel:"noopener noreferrer"},d=r(`

    用户可以通过 palette 文件 来自定义样式变量,还可以通过 style 文件 来添加额外的样式。

    Palette 文件

    Palette 文件的路径是 .vuepress/styles/palette.scss

    你可以利用它来覆盖默认主题的预定义 SASS 变量。

    点击查看 SASS 变量
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style 文件

    Style 文件的路径是 .vuepress/styles/index.scss

    你可以在这里添加额外的样式,或者覆盖默认样式:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    你也可以利用它来覆盖默认主题的预定义 CSS 变量。

    点击查看 CSS 变量
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.back-to-top {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +.pwa-popup {
    +  --pwa-popup-text-color: var(--c-text);
    +  --pwa-popup-bg-color: var(--c-bg);
    +  --pwa-popup-border-color: var(--c-brand);
    +  --pwa-popup-shadow: 0 4px 16px var(--c-brand);
    +  --pwa-popup-btn-text-color: var(--c-bg);
    +  --pwa-popup-btn-bg-color: var(--c-brand);
    +  --pwa-popup-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    点击查看暗黑模式 CSS 变量
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    `,12);function C(v,b){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return c(),D("div",null,[i,l(p,{package:"@vuepress/theme-default"}),s("p",null,[n("默认主题使用 "),s("a",y,[n("SASS"),l(o)]),n(" 作为 CSS 预处理器。")]),d])}const u=e(t,[["render",C],["__file","styles.html.vue"]]);export{u as default}; diff --git a/assets/styles.html-wHbF_pty.js b/assets/styles.html-wHbF_pty.js new file mode 100644 index 0000000000..9bf041e31a --- /dev/null +++ b/assets/styles.html-wHbF_pty.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4534c7cc","path":"/zh/themes/default/styles.html","title":"样式","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Palette 文件","slug":"palette-文件","link":"#palette-文件","children":[]},{"level":2,"title":"Style 文件","slug":"style-文件","link":"#style-文件","children":[]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/themes/default/styles.md"}');export{e as data}; diff --git a/assets/styles.html-za_WSAv-.js b/assets/styles.html-za_WSAv-.js new file mode 100644 index 0000000000..dd758eeeb2 --- /dev/null +++ b/assets/styles.html-za_WSAv-.js @@ -0,0 +1,232 @@ +import{_ as e,r as a,o as c,c as D,a as l,b as s,d as n,e as r}from"./app-GUhkEPRO.js";const t={},i=s("h1",{id:"styles",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#styles"},[s("span",null,"Styles")])],-1),y={href:"https://sass-lang.com/",target:"_blank",rel:"noopener noreferrer"},d=r(`

    Users can customize style variables via a palette file, and add extra styles via a style file.

    Palette File

    The path of the palette file is .vuepress/styles/palette.scss.

    You can make use of it to override predefined SASS variables of the default theme.

    Click to expand SASS variables
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style File

    The path of the style file is .vuepress/styles/index.scss.

    You can add extra styles here, or override the default styles:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    You can also make use of it to override predefined CSS variables of the default theme.

    Click to expand CSS variables
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.back-to-top {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +.pwa-popup {
    +  --pwa-popup-text-color: var(--c-text);
    +  --pwa-popup-bg-color: var(--c-bg);
    +  --pwa-popup-border-color: var(--c-brand);
    +  --pwa-popup-shadow: 0 4px 16px var(--c-brand);
    +  --pwa-popup-btn-text-color: var(--c-bg);
    +  --pwa-popup-btn-bg-color: var(--c-brand);
    +  --pwa-popup-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    Click to expand dark mode CSS variables
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    `,12);function C(v,b){const p=a("NpmBadge"),o=a("ExternalLinkIcon");return c(),D("div",null,[i,l(p,{package:"@vuepress/theme-default"}),s("p",null,[n("The default theme uses "),s("a",y,[n("SASS"),l(o)]),n(" as the CSS pre-processor.")]),d])}const u=e(t,[["render",C],["__file","styles.html.vue"]]);export{u as default}; diff --git a/assets/theme-data.html-5YmilAfL.js b/assets/theme-data.html-5YmilAfL.js new file mode 100644 index 0000000000..3baed8c903 --- /dev/null +++ b/assets/theme-data.html-5YmilAfL.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-f3866458","path":"/zh/plugins/theme-data.html","title":"theme-data","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"themeData","slug":"themedata","link":"#themedata","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useThemeData","slug":"usethemedata","link":"#usethemedata","children":[]},{"level":3,"title":"useThemeLocaleData","slug":"usethemelocaledata","link":"#usethemelocaledata","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/theme-data.md"}');export{e as data}; diff --git a/assets/theme-data.html-6IZWf1rZ.js b/assets/theme-data.html-6IZWf1rZ.js new file mode 100644 index 0000000000..707280e59e --- /dev/null +++ b/assets/theme-data.html-6IZWf1rZ.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4091b4e3","path":"/plugins/theme-data.html","title":"theme-data","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"themeData","slug":"themedata","link":"#themedata","children":[]}]},{"level":2,"title":"Composition API","slug":"composition-api","link":"#composition-api","children":[{"level":3,"title":"useThemeData","slug":"usethemedata","link":"#usethemedata","children":[]},{"level":3,"title":"useThemeLocaleData","slug":"usethemelocaledata","link":"#usethemelocaledata","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/theme-data.md"}');export{e as data}; diff --git a/assets/theme-data.html-DRSBadNU.js b/assets/theme-data.html-DRSBadNU.js new file mode 100644 index 0000000000..418fd508e2 --- /dev/null +++ b/assets/theme-data.html-DRSBadNU.js @@ -0,0 +1,51 @@ +import{_ as o,r as n,o as t,c as i,a as e,b as s,d as a,w as c,e as r}from"./app-GUhkEPRO.js";const D={},d=s("h1",{id:"theme-data",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme-data"},[s("span",null,"theme-data")])],-1),u=r(`

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

    For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.

    Usage

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

    Options

    themeData

    • Type: ThemeData

    • Default: {}

    • Details:

      The theme data object that you want to use in client side.

      You can provide theme data in Node side via this option, and use it in client side via useThemeData and useThemeLocaleData.

    • Example:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    WARNING

    The theme data object will be processed by JSON.stringify() before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.

    Composition API

    useThemeData

    • Details:

      Returns the theme data ref object.

      The value is provided by themeData option.

    • Example:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • Details:

      Returns the theme data ref object in current locale.

      The properties of current locale has been merged into the root-level properties.

    • Example:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    `,17);function y(m,v){const l=n("NpmBadge"),p=n("RouterLink");return t(),i("div",null,[d,e(l,{package:"@vuepress/plugin-theme-data"}),s("p",null,[a("Provide client data for your theme, with VuePress "),e(p,{to:"/guide/i18n.html"},{default:c(()=>[a("i18n")]),_:1}),a(" support.")]),u])}const C=o(D,[["render",y],["__file","theme-data.html.vue"]]);export{C as default}; diff --git a/assets/theme-data.html-ufOo3grp.js b/assets/theme-data.html-ufOo3grp.js new file mode 100644 index 0000000000..af407dbf35 --- /dev/null +++ b/assets/theme-data.html-ufOo3grp.js @@ -0,0 +1,51 @@ +import{_ as o,r as n,o as t,c,a as l,b as s,d as a,w as i,e as r}from"./app-GUhkEPRO.js";const D={},d=s("h1",{id:"theme-data",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#theme-data"},[s("span",null,"theme-data")])],-1),y=r(`

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

    对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。

    使用方法

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

    配置项

    themeData

    • 类型: ThemeData

    • 默认值: {}

    • 详情:

      你希望在 Client 端中使用的主题数据对象。

      你可以通过该配置项,在 Node 端提供主题数据,然后在客户端通过 useThemeDatauseThemeLocaleData 来使用主题数据。

    • 示例:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    注意

    主题数据对象在传递到客户端之前,会使用 JSON.stringify() 进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。

    Composition API

    useThemeData

    • 详情:

      返回主题数据的 Ref 对象。

      数据是通过 themeData 配置项提供的。

    • 示例:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • 详情:

      返回当前 locale 下主题数据的 Ref 对象。

      当前 locale 中的字段已被合并到顶层字段中。

    • 示例:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    `,17);function u(m,v){const e=n("NpmBadge"),p=n("RouterLink");return t(),c("div",null,[d,l(e,{package:"@vuepress/plugin-theme-data"}),s("p",null,[a("为你的主题提供客户端数据,包含 VuePress 的 "),l(p,{to:"/guide/i18n.html"},{default:i(()=>[a("多语言支持")]),_:1}),a(" 。")]),y])}const h=o(D,[["render",u],["__file","theme-data.html.vue"]]);export{h as default}; diff --git a/assets/toc.html-c4CKbH_A.js b/assets/toc.html-c4CKbH_A.js new file mode 100644 index 0000000000..8d2203a7d8 --- /dev/null +++ b/assets/toc.html-c4CKbH_A.js @@ -0,0 +1,74 @@ +import{_ as i,r as o,o as c,c as r,a,b as n,d as s,w as e,e as p}from"./app-GUhkEPRO.js";const d={},D=n("h1",{id:"toc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#toc"},[n("span",null,"toc")])],-1),u=p(`

    This plugin will provide a table-of-contents (TOC) component.

    Usage

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

    Differences with Markdown TOC Syntax

    `,5),y=p(`
    <!-- markdown toc syntax -->
    +
    +[[toc]]
    +
    +<!-- vue toc component -->
    +<Toc />
    +

    Both of them can be pre-rendered correctly in build mode. However, there are some differences between them.

    The markdown syntax [[toc]] could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content.

    The component <Toc/> could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component.

    `,4),v=n("code",null,"linkClass",-1),C=n("code",null,"linkActiveClass",-1),m=p(`

    Therefore, this plugin is more useful for theme developers.

    Options

    componentName

    • Type: string

    • Default: 'Toc'

    • Details:

      Specify the name of the TOC component.

    defaultPropsOptions

    • Type: Partial<TocPropsOptions>

    • Default: {}

    • Details:

      Override the default values of the component options prop.

    Component Props

    The TOC component also accepts props for customization.

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • Type: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • Details:

      Specify the headers array to render.

      If this prop is not specified, the headers of current page will be used.

    options

    • Type: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouterLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • Details:

      Customize the TOC component.

      If the containerTag is set to an empty string '', the <nav> container will be removed totally.

    • Example:

      The rendered TOC component with default options looks like:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouterLink class="vuepress-toc-link" to="#foo">Foo</RouterLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouterLink class="vuepress-toc-link active" to="#bar">Bar</RouterLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouterLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouterLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    `,20);function h(b,g){const t=o("NpmBadge"),l=o("RouterLink");return c(),r("div",null,[D,a(t,{package:"@vuepress/plugin-toc"}),u,n("p",null,[s("Similar to the "),a(l,{to:"/guide/markdown.html#table-of-contents"},{default:e(()=>[s("Table of Contents Markdown Syntax")]),_:1}),s(", the TOC component that provided by this plugin could be used in your markdown content directly:")]),y,n("p",null,[s("This plugin could work together with "),a(l,{to:"/plugins/active-header-links.html"},{default:e(()=>[s("@vuepress/plugin-active-header-links")]),_:1}),s(" by setting the "),a(l,{to:"/plugins/active-header-links.html#headerlinkselector"},{default:e(()=>[s("headerLinkSelector")]),_:1}),s(" to match the "),v,s(" option. When the page scroll to a certain header anchor, this corresponding link will be added "),C,s(" class name.")]),m])}const k=i(d,[["render",h],["__file","toc.html.vue"]]);export{k as default}; diff --git a/assets/toc.html-gNCC-lB2.js b/assets/toc.html-gNCC-lB2.js new file mode 100644 index 0000000000..5c5d5f08f1 --- /dev/null +++ b/assets/toc.html-gNCC-lB2.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-57f70f48","path":"/zh/plugins/toc.html","title":"toc","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"使用方法","slug":"使用方法","link":"#使用方法","children":[]},{"level":2,"title":"与 Markdown 目录语法的区别","slug":"与-markdown-目录语法的区别","link":"#与-markdown-目录语法的区别","children":[]},{"level":2,"title":"配置项","slug":"配置项","link":"#配置项","children":[{"level":3,"title":"componentName","slug":"componentname","link":"#componentname","children":[]},{"level":3,"title":"defaultPropsOptions","slug":"defaultpropsoptions","link":"#defaultpropsoptions","children":[]}]},{"level":2,"title":"组件 Props","slug":"组件-props","link":"#组件-props","children":[{"level":3,"title":"headers","slug":"headers","link":"#headers","children":[]},{"level":3,"title":"options","slug":"options","link":"#options","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"zh/plugins/toc.md"}');export{e as data}; diff --git a/assets/toc.html-sozxNRF7.js b/assets/toc.html-sozxNRF7.js new file mode 100644 index 0000000000..d5f9e5c4f2 --- /dev/null +++ b/assets/toc.html-sozxNRF7.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-862929ce","path":"/plugins/toc.html","title":"toc","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage","link":"#usage","children":[]},{"level":2,"title":"Differences with Markdown TOC Syntax","slug":"differences-with-markdown-toc-syntax","link":"#differences-with-markdown-toc-syntax","children":[]},{"level":2,"title":"Options","slug":"options","link":"#options","children":[{"level":3,"title":"componentName","slug":"componentname","link":"#componentname","children":[]},{"level":3,"title":"defaultPropsOptions","slug":"defaultpropsoptions","link":"#defaultpropsoptions","children":[]}]},{"level":2,"title":"Component Props","slug":"component-props","link":"#component-props","children":[{"level":3,"title":"headers","slug":"headers","link":"#headers","children":[]},{"level":3,"title":"options","slug":"options-1","link":"#options-1","children":[]}]}],"git":{"updatedTime":1706605723000,"contributors":[{"name":"Mr.Hope","email":"mister-hope@outlook.com","commits":1}]},"filePathRelative":"plugins/toc.md"}');export{e as data}; diff --git a/assets/toc.html-t9K5uGlI.js b/assets/toc.html-t9K5uGlI.js new file mode 100644 index 0000000000..6783488620 --- /dev/null +++ b/assets/toc.html-t9K5uGlI.js @@ -0,0 +1,74 @@ +import{_ as c,r as o,o as i,c as r,a,b as n,d as s,w as e,e as p}from"./app-GUhkEPRO.js";const d={},D=n("h1",{id:"toc",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#toc"},[n("span",null,"toc")])],-1),y=p(`

    该插件会提供一个目录 (table-of-contents, TOC) 组件。

    使用方法

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

    与 Markdown 目录语法的区别

    `,5),u=p(`
    <!-- Markdown 目录语法 -->
    +
    +[[toc]]
    +
    +<!-- Vue 目录组件 -->
    +<Toc />
    +

    在 Build 模式中,它们都可以被正确地预渲染。然而,它们之间存在一些区别。

    Markdown 语法 [[toc]] 仅能在 Markdown 文件中使用。它是由 markdown-it 解析的,生成的目录是静态内容。

    组件 <Toc/> 既可以用在 Markdown 文件中,也可以用在 Vue 文件中。它是由 Vue 加载的,生成的目录是一个 Vue 组件。

    `,4),v=n("code",null,"linkClass",-1),C=n("code",null,"linkActiveClass",-1),m=p(`

    因此,该插件对于主题开发者来说更为有用。

    配置项

    componentName

    • 类型: string

    • 默认值: 'Toc'

    • 详情:

      指定目录组件的名称。

    defaultPropsOptions

    • 类型: Partial<TocPropsOptions>

    • 默认值: {}

    • 详情:

      覆盖组件 options Prop 的默认值。

    组件 Props

    目录组件可以通过 Props 来进行自定义。

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • 类型: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • 详情:

      指定要渲染的标题数组。

      如果该 Prop 没有被设置,默认会使用当前页面的标题。

    options

    • 类型: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouterLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • 详情:

      自定义目录组件。

      如果 containerTag 设置为空字符串 '' ,那么最外层的 <nav> Container 会被完全移除。

    • 示例:

      使用默认 options 的目录组件的渲染结果类似以下结构:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouterLink class="vuepress-toc-link" to="#foo">Foo</RouterLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouterLink class="vuepress-toc-link active" to="#bar">Bar</RouterLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouterLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouterLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    `,20);function b(E,h){const t=o("NpmBadge"),l=o("RouterLink");return i(),r("div",null,[D,a(t,{package:"@vuepress/plugin-toc"}),y,n("p",null,[s("与 "),a(l,{to:"/guide/markdown.html#%E7%9B%AE%E5%BD%95"},{default:e(()=>[s("Markdown 目录语法")]),_:1}),s(" 类似,该插件提供的目录组件可以直接在你的 Markdown 内容中使用:")]),u,n("p",null,[s("该插件可以和 "),a(l,{to:"/zh/plugins/active-header-links.html"},{default:e(()=>[s("@vuepress/plugin-active-header-links")]),_:1}),s(" 协同工作,你只需要将 "),a(l,{to:"/zh/plugins/active-header-links.html#headerlinkselector"},{default:e(()=>[s("headerLinkSelector")]),_:1}),s(" 与该插件的 "),v,s(" 匹配即可。当页面滚动至某个标题锚点后,对应的链接就会被加上 "),C,s(" 类名。")]),m])}const k=c(d,[["render",b],["__file","toc.html.vue"]]);export{k as default}; diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000..e481e5dda7 Binary files /dev/null and b/favicon.ico differ diff --git a/images/cookbook/extending-a-theme-01.png b/images/cookbook/extending-a-theme-01.png new file mode 100644 index 0000000000..9ba6d7e812 Binary files /dev/null and b/images/cookbook/extending-a-theme-01.png differ diff --git a/images/hero.png b/images/hero.png new file mode 100644 index 0000000000..ac6beaff06 Binary files /dev/null and b/images/hero.png differ diff --git a/images/icons/android-chrome-192x192.png b/images/icons/android-chrome-192x192.png new file mode 100644 index 0000000000..ddd043910e Binary files /dev/null and b/images/icons/android-chrome-192x192.png differ diff --git a/images/icons/android-chrome-384x384.png b/images/icons/android-chrome-384x384.png new file mode 100644 index 0000000000..86e1fd58b3 Binary files /dev/null and b/images/icons/android-chrome-384x384.png differ diff --git a/images/icons/apple-touch-icon.png b/images/icons/apple-touch-icon.png new file mode 100644 index 0000000000..208915f1de Binary files /dev/null and b/images/icons/apple-touch-icon.png differ diff --git a/images/icons/favicon-16x16.png b/images/icons/favicon-16x16.png new file mode 100644 index 0000000000..ca5047e7b8 Binary files /dev/null and b/images/icons/favicon-16x16.png differ diff --git a/images/icons/favicon-32x32.png b/images/icons/favicon-32x32.png new file mode 100644 index 0000000000..e275ce9ba1 Binary files /dev/null and b/images/icons/favicon-32x32.png differ diff --git a/images/icons/mstile-150x150.png b/images/icons/mstile-150x150.png new file mode 100644 index 0000000000..d0b1439483 Binary files /dev/null and b/images/icons/mstile-150x150.png differ diff --git a/images/icons/safari-pinned-tab.svg b/images/icons/safari-pinned-tab.svg new file mode 100644 index 0000000000..dc0b992c04 --- /dev/null +++ b/images/icons/safari-pinned-tab.svg @@ -0,0 +1,23 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/images/logo.png b/images/logo.png new file mode 100644 index 0000000000..60e17006ad Binary files /dev/null and b/images/logo.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000000..1c5d639569 --- /dev/null +++ b/index.html @@ -0,0 +1,37 @@ + + + + + + + + + Home | VuePress Ecosystem + + + + + + + + + diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000000..d2e935f1ae --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "VuePress", + "short_name": "VuePress", + "description": "Vue-powered Static Site Generator", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#fff", + "theme_color": "#3eaf7c", + "icons": [ + { + "src": "/images/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/images/icons/android-chrome-384x384.png", + "sizes": "384x384", + "type": "image/png" + } + ] +} diff --git a/plugins/active-header-links.html b/plugins/active-header-links.html new file mode 100644 index 0000000000..ace44f4a00 --- /dev/null +++ b/plugins/active-header-links.html @@ -0,0 +1,47 @@ + + + + + + + + + active-header-links | VuePress Ecosystem + + + + + +

    active-header-links

    @vuepress/plugin-active-header-links

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

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

    Usage

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

    Options

    headerLinkSelector

    • Type: string

    • Default: 'a.sidebar-item'

    • Details:

      Selector of header link.

      If a header anchor does not have a corresponding header link, this plugin won't change the route hash to that anchor when scrolling to it.

    headerAnchorSelector

    delay

    • Type: number

    • Default: 200

    • Details:

      The delay of the debounced scroll event listener.

    offset

    • Type: number

    • Default: 5

    • Details:

      Even if you click the link of the header anchor directly, the scrollTop might not be exactly equal to offsetTop of the header anchor, so we add an offset to avoid the error.

    + + + diff --git a/plugins/back-to-top.html b/plugins/back-to-top.html new file mode 100644 index 0000000000..ec32f50e6e --- /dev/null +++ b/plugins/back-to-top.html @@ -0,0 +1,51 @@ + + + + + + + + + back-to-top | VuePress Ecosystem + + + + + +

    back-to-top

    @vuepress/plugin-back-to-top

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

    This plugin has been integrated into the default theme.

    Usage

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

    Options

    threshold

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

    progress

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

    Styles

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

    :root {
    +  --back-to-top-z-index: 5;
    +  --back-to-top-icon: url("back-to-top.svg");
    +  --back-to-top-bg-color: #fff;
    +  --back-to-top-color: #3eaf7c;
    +  --back-to-top-color-hover: #71cda3;
    +  --back-to-top-shadow: rgb(0 0 0 / 20%);
    +}
    +
    + + + diff --git a/plugins/container.html b/plugins/container.html new file mode 100644 index 0000000000..63316375e5 --- /dev/null +++ b/plugins/container.html @@ -0,0 +1,75 @@ + + + + + + + + + container | VuePress Ecosystem + + + + + +

    container

    @vuepress/plugin-container

    Register markdown custom containers in your VuePress site.

    This plugin simplifies the use of markdown-it-containeropen in new window, but also retains its original capabilities.

    The Custom Containers of default theme is powered by this plugin.

    Usage

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

    Container Syntax

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

    TIP

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

    Options

    type

    locales

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

    • Details:

      The default info of the container in different locales.

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

    • Example:

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

    before

    • Type: (info: string) => string

    • Default:

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

      A function to render the starting tag of the container.

      The first param is the info part of container syntax.

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

    after

    • Type: (info: string) => string

    • Default:

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

      A function to render the ending tag of the container.

      The first param is the info part of container syntax.

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

    render

    • Type:
    type MarkdownItContainerRenderFunction = (
    +  tokens: Token[],
    +  index: number,
    +  options: any,
    +  env: MarkdownEnv,
    +  self: Renderer,
    +) => string
    +

    validate

    marker

    + + + diff --git a/plugins/copy-code.html b/plugins/copy-code.html new file mode 100644 index 0000000000..60b7b8b1cc --- /dev/null +++ b/plugins/copy-code.html @@ -0,0 +1,97 @@ + + + + + + + + + copy-code | VuePress Ecosystem + + + + + +

    copy-code

    @vuepress/plugin-copy-code

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

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

    Usage

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

    Options

    selector

    • Type: string | string[]

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

    • Details:

      Code block selector

    showInMobile

    • Type: boolean

    • Default: false

    • Details:

      Whether to display copy button on the mobile device

    duration

    • Type: number

    • Default: 2000

    • Details:

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

    delay

    • Type: number

    • Default: 800

    • Details:

      The delay of registering copy code buttons, in ms.

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

    locales

    • Type: CopyCodeLocaleConfig

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

    • Details:

      Locales config for copy code plugin.

    • Example:

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

    Styles

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

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: var(--code-ln-color, #9e9e9e);
    +  --copy-code-hover: var(--code-hl-bg-color, rgb(0 0 0 / 66%));
    +}
    +
    + + + diff --git a/plugins/copyright.html b/plugins/copyright.html new file mode 100644 index 0000000000..9b7e67ca9c --- /dev/null +++ b/plugins/copyright.html @@ -0,0 +1,102 @@ + + + + + + + + + copyright | VuePress Ecosystem + + + + + +

    copyright

    @vuepress/plugin-copyright

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

    Usage

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

    This plugin is disabled globally by default, you can:

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

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

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

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

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

    Disable Copy and Selection

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

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

    Options

    author

    • Type: string
    • Details: Default author Information

    license

    • Type: string
    • Details: Default license Information

    authorGetter

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

    licenseGetter

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

    copyrightGetter

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

    triggerLength

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

    maxLength

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

    global

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

    disableCopy

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

    disableSelection

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

    canonical

    • Type: string
    • Details: Canonical deploy location.

    Example

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

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

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

    locales

    • Type: CopyrightLocaleConfig

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

    • Example:

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

    Frontmatter

    copy.triggerLength

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

    copy.maxLength

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

    copy.disableCopy

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

    copy.disableSelection

    • Type: boolean
    • Default: false
    • Details: Disable selection
    + + + diff --git a/plugins/docsearch.html b/plugins/docsearch.html new file mode 100644 index 0000000000..3c78494bc9 --- /dev/null +++ b/plugins/docsearch.html @@ -0,0 +1,241 @@ + + + + + + + + + docsearch | VuePress Ecosystem + + + + + +

    docsearch

    @vuepress/plugin-docsearch

    Integrate Algolia DocSearchopen in new window into VuePress, which can provide search to your documentation site.

    TIP

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

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

    Usage

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

    Get Search Index

    You need to submit the URL of your siteopen in new window to join the DocSearch program. The DocSearch team will send apiKey and indexName to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress.

    Alternatively, you can run your own crawleropen in new window to generate the index, and then use your own appId, apiKey and indexName to configure this plugin.

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


































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     








     
































































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

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

    TIP

    If you are not using default theme, or you meet any problems when using docsearch, you can also check the above example crawler config, and ahead to Algolia Crawleropen in new window, and edit your config with 'Editor' panel in project sidebar.

    Options

    apiKey

    indexName

    appId

    searchParameters

    placeholder

    disableUserPersonalization

    initialQuery

    translations

    locales

    • Type: Record<string, DocsearchPluginOptions>

    • Details:

      Options of this plugin in different locales.

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

    • Example:

    export default {
    +  plugins: [
    +    docsearchPlugin({
    +      appId: '<APP_ID>',
    +      apiKey: '<API_KEY>',
    +      indexName: '<INDEX_NAME>',
    +      locales: {
    +        '/': {
    +          placeholder: 'Search Documentation',
    +          translations: {
    +            button: {
    +              buttonText: 'Search Documentation',
    +            },
    +          },
    +        },
    +        '/zh/': {
    +          placeholder: '搜索文档',
    +          translations: {
    +            button: {
    +              buttonText: '搜索文档',
    +            },
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    indexBase

    • Type: string

    • Default: base

    • Details:

      The base path of the search index.

      If you are deploying your site to multiple domains, you don't need to submit all of them to DocSearch and generate search index separately. You could choose one of the domains as the index domain, and only submit the index domain to Docsearch for crawling search index. Then, you could reuse the search index across all deployments.

      However, if the base of your deployments are different for different domains, you need to set the option to the base of your index domain, so that other deployments could reuse the search index correctly.

    injectStyles

    • Type: boolean

    • Default: true

    • Details:

      Whether to inject the default styles of DocSearch or not.

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

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

    Styles

    You can customize styles via CSS variables that provided by @docsearch/cssopen in new window:

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

    Components

    Docsearch

    • Details:

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

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

    TIP

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

    + + + diff --git a/plugins/external-link-icon.html b/plugins/external-link-icon.html new file mode 100644 index 0000000000..974cef55a4 --- /dev/null +++ b/plugins/external-link-icon.html @@ -0,0 +1,64 @@ + + + + + + + + + external-link-icon | VuePress Ecosystem + + + + + +

    external-link-icon

    @vuepress/plugin-external-link-icon

    This plugin will add an icon to the external link in your markdown content, i.e. open in new window

    This plugin has been integrated into the default theme.

    Usage

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

    Options

    locales

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

    • Details:

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

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

    • Example:

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

    Frontmatter

    externalLinkIcon

    • Type: boolean

    • Details:

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

    Styles

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

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

    Components

    ExternalLinkIcon

    • Details:

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

    TIP

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

    + + + diff --git a/plugins/feed/channel.html b/plugins/feed/channel.html new file mode 100644 index 0000000000..5f999d2dbd --- /dev/null +++ b/plugins/feed/channel.html @@ -0,0 +1,51 @@ + + + + + + + + + Channel Config | VuePress Ecosystem + + + + + +

    Channel Config

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

    channel.title

    • Type: string
    • Default: SiteConfig.title

    Channel title

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

    Channel address

    channel.description

    • Type: string
    • Default: SiteConfig.description

    Channel description

    channel.language

    • Type: string

    • Default:

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

    The language of the channel

    • Type: string

    • Default:

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

    Channel copyright information

    channel.pubDate

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

    Publish date of the Channel

    channel.lastUpdated

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

    Last update time of channel content

    channel.ttl

    • Type: number
    • Recommended to set manually: Yes

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

    channel.image

    • Type: string
    • Recommended to set manually: Yes

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

    channel.icon

    • Type: string
    • Recommended to set manually: Yes

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

    channel.author

    • Type: FeedAuthor
    • Recommended to set manually: Yes

    The author of the channel.

    FeedAuthor format
    interface FeedAuthor {
    +  /** Author name */
    +  name: string
    +  /** Author's email */
    +  email?: string
    +  /** Author's site */
    +  url?: string
    +  /**
    +   * Author's avatar address
    +   *
    +   * Square, preferably not less than 128×128 with transparent background
    +   */
    +  avatar?: string
    +}
    +

    channel.hub

    • Type: string

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

    WebSub

    For details, see Websubopen in new window.

    + + + diff --git a/plugins/feed/config.html b/plugins/feed/config.html new file mode 100644 index 0000000000..ad1eaa2ade --- /dev/null +++ b/plugins/feed/config.html @@ -0,0 +1,54 @@ + + + + + + + + + Plugin Config | VuePress Ecosystem + + + + + +

    Plugin Config

    hostname

    • Type: string
    • Required: Yes

    The domain name of the deployment site.

    atom

    • Type: boolean
    • Default: false

    Whether to output Atom syntax files.

    json

    • Type: boolean
    • Default: false

    Whether output JSON syntax files.

    rss

    • Type: boolean
    • Default: false

    Whether to output RSS syntax files.

    image

    • Type: string

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

    icon

    • Type: string

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

    count

    • Type: number
    • Default: 100

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

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

    preservedElements

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

    Custom element or component which should be preserved in feed.

    By default, all unknown tags will be removed.

    filter

    • Type: (page: Page)=> boolean

    • Default:

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

    A custom filter function, used to filter feed items.

    sorter

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

    • Default:

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

    Custom sorter function for feed items.

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

    TIP

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

    channel

    channel option is used to config Feed Channels.

    For available options, please see Config → Channel

    devServer

    • Type: boolean
    • Default: false

    Whether enabled in devServer.

    TIP

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

    devHostname

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

    Hostname to use in devServer

    atomOutputFilename

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

    Atom syntax output filename, relative to dest folder.

    atomXslTemplate

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

    Atom xsl template file content.

    atomXslFilename

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

    Atom xsl filename, relative to dest folder.

    jsonOutputFilename

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

    JSON syntax output filename, relative to dest folder.

    rssOutputFilename

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

    RSS syntax output filename, relative to dest folder.

    rssXslTemplate

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

    RSS xsl template file content.

    rssXslFilename

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

    RSS syntax xsl filename, relative to dest folder.

    getter

    Feed generation controller, see Feed Getter.

    The plugin has a built-in getter, only set this if you want full control of feed generation.

    locales

    • Type: Record<string, BaseFeedOptions>

    You can use it to specific options for each locale.

    Any options above are supported except hostname.

    + + + diff --git a/plugins/feed/frontmatter.html b/plugins/feed/frontmatter.html new file mode 100644 index 0000000000..13f4e9a786 --- /dev/null +++ b/plugins/feed/frontmatter.html @@ -0,0 +1,87 @@ + + + + + + + + + Frontmatter Config | VuePress Ecosystem + + + + + +

    Frontmatter Config

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

    Additions and Removals

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

    Frontmatter Information

    title

    • Type: string

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

    description

    • Type: string

    Description of the page

    date

    • Type: Date

    Date when the page was published

    article

    • Type: boolean

    Whether the page is an article

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

    • Type: string

    Page copyright information

    cover / image / banner

    • Type: string

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

    Frontmatter Options

    feed.title

    • Type: string

    The title of the feed item

    feed.description

    • Type: string

    Description of the feed item

    feed.content

    • Type: string

    The content of the feed item

    feed.author

    • Type: FeedAuthor[] | FeedAuthor

    The author of the feed item

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

    feed.contributor

    • Type: FeedContributor[] | FeedContributor

    Contributors to feed item

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

    feed.guid

    • Type: string

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

    You should ensure every feed has a unique guid.

    + + + diff --git a/plugins/feed/getter.html b/plugins/feed/getter.html new file mode 100644 index 0000000000..9762c6c70b --- /dev/null +++ b/plugins/feed/getter.html @@ -0,0 +1,127 @@ + + + + + + + + + Feed Getter | VuePress Ecosystem + + + + + +

    Feed Getter

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

    getter.title

    • Type: (page: Page) => string

    Item title getter

    • Type: (page: Page) => string

    Item link getter

    getter.description

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

    Item description getter

    TIP

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

    getter.content

    • Type: (page: Page) => string

    Item content getter

    getter.author

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

    Item author getter.

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

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

    getter.category

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

    Item category getter.

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

    getter.enclosure

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

    Item enclosure getter.

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

    getter.publishDate

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

    Item release date getter

    getter.lastUpdateDate

    • Type: (page: Page) => Date

    Item last update date getter

    getter.image

    • Type: (page: Page) => string

    Item Image Getter

    Ensure it's returning a full URL

    getter.contributor

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

    Item Contributor Getter

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

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

    Item copyright getter

    + + + diff --git a/plugins/feed/guide.html b/plugins/feed/guide.html new file mode 100644 index 0000000000..c710f69e90 --- /dev/null +++ b/plugins/feed/guide.html @@ -0,0 +1,37 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    Usage

    The plugin can generate feed files in the following three formats for you:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    Please set atom, json or rss to true in the plugin options according to the formats you want to generate.

    To correctly generate feed links, you need to set hostname in the plugin options,

    Readable Preview

    When you open the feed file in browser, we magically convert atom and rss feed xml to human readable html via xsl template. Check atomopen in new window and rssopen in new window feed of this site as an example!

    If you want to preview your feed in devServer, set devServer: true in plugin options. You may also need to set devHostname if you are not using the default http://localhost:{port}.

    Channel settings

    You can customize the feed channel information by setting the channel option.

    We recommend the following settings:

    • Convert the date of creating the feed to ISOString and write it into channel.pubDate
    • The update period of the content set in channel.ttl (unit: minutes)
    • Set copyright information via channel.copyright
    • Set the channel author via channel.author.

    For detailed options and their default values, see Channel Config

    Feed Generation

    By default, all articles are added to the feed stream.

    You can set feed and other options in page frontmatter to control contents of feed item. See Frontmatter Config for how they are converted.

    You can take full control of feed items generation by configuring the getter in the plugin options. For detailed options and their default values, see Configuration → Feed Getter.

    I18n Config

    The plugin generates separate feeds for each language.

    You can provide different settings for different languages via locales in the plugin options.

    + + + diff --git a/plugins/feed/index.html b/plugins/feed/index.html new file mode 100644 index 0000000000..69ef5a1487 --- /dev/null +++ b/plugins/feed/index.html @@ -0,0 +1,47 @@ + + + + + + + + + feed | VuePress Ecosystem + + + + + +

    feed

    @vuepress/plugin-feed

    Usage

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/git.html b/plugins/git.html new file mode 100644 index 0000000000..45fd648520 --- /dev/null +++ b/plugins/git.html @@ -0,0 +1,66 @@ + + + + + + + + + git | VuePress Ecosystem + + + + + +

    git

    @vuepress/plugin-git

    This plugin will collect git information of your pages, including the created and updated time, the contributors, etc.

    The lastUpdated and contributors of default theme is powered by this plugin.

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

    Usage

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

    Git Repository

    This plugin requires your project to be inside a Git Repositoryopen in new window, so that it can collect information from the commit history.

    You should ensure all commits are available when building your site. For example, CI workflows usually clone your repository with --depth 1open in new window to avoid fetching all commits, so you should disable the behavior to make this plugin work properly in CI.

    WARNING

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

    Options

    createdTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page created time or not.

    updatedTime

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page updated time or not.

    contributors

    • Type: boolean

    • Default: true

    • Details:

      Whether to collect page contributors or not.

    Frontmatter

    gitInclude

    • Type: string[]

    • Details:

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

    • Example:

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

    Page Data

    This plugin will add a git field to page data.

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

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

    git.createdTime

    • Type: number

    • Details:

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

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

    git.updatedTime

    • Type: number

    • Details:

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

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

    git.contributors

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

      The contributors information of the page.

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

    + + + diff --git a/plugins/google-analytics.html b/plugins/google-analytics.html new file mode 100644 index 0000000000..c16ef4e414 --- /dev/null +++ b/plugins/google-analytics.html @@ -0,0 +1,62 @@ + + + + + + + + + google-analytics | VuePress Ecosystem + + + + + +

    google-analytics

    @vuepress/plugin-google-analytics

    Integrate Google Analyticsopen in new window into VuePress.

    This plugin will import gtag.jsopen in new window for Google Analytics 4open in new window.

    Usage

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

    Reporting Events

    Google Analytics will automatically collect some eventsopen in new window, such as page_view, first_visit, etc.

    So if you only want to collect some basic data of your site, you don't need to do anything else except setting the Measurement ID correctly.

    After using this plugin, the global gtag() function is available on the window object, and you can use it for custom events reportingopen in new window.

    Options

    id

    • Type: string

    • Details:

      The Measurement ID of Google Analytics 4, which should start with 'G-'.

      You can follow the instructions hereopen in new window to find your Measurement ID. Notice the difference between Google Analytics 4 Measurement ID (i.e. "G-" ID) and Universal Analytics Tracking ID (i.e. "UA-" ID).

    • Example:

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +    }),
    +  ],
    +}
    +

    debug

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/medium-zoom.html b/plugins/medium-zoom.html new file mode 100644 index 0000000000..e644a3e158 --- /dev/null +++ b/plugins/medium-zoom.html @@ -0,0 +1,67 @@ + + + + + + + + + medium-zoom | VuePress Ecosystem + + + + + +

    medium-zoom

    @vuepress/plugin-medium-zoom

    Integrate medium-zoomopen in new window into VuePress, which can provide the ability to zoom images.

    This plugin has been integrated into the default theme.

    Usage

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

    Options

    selector

    • Type: string

    • Default: ':not(a) > img'

    • Details:

      Selector of zoomable images.

      By default this plugin will make all images zoomable except those inside <a> tags.

    delay

    • Type: number

    • Default: 500

    • Details:

      Delay in milliseconds.

      After navigating to a new page, this plugin will make images zoomable with a delay.

    zoomOptions

    Styles

    You can customize most of the zoom styles via zoomOptions, while this plugin also provides some CSS variables for additional customization:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    • Details:

      Returns the Zoom instance that used by this plugin, so that you can use the instance methodsopen in new window directly.

      This plugin will make images zoomable after navigating to current page. But if you are going to add new images dynamically, you may need this method to make those new images zoomable, too.

      This plugin adds an extra refresh method on the Zoom instance, which will call zoom.detach() then zoom.attach() with the selector as the default parameter. It will help you to refresh the zoomable images for current page.

    • Example:

    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... do something to add new images in current page
    +
    +    // then you may need to call `refresh` manually to make those new images zoomable
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    + + + diff --git a/plugins/nprogress.html b/plugins/nprogress.html new file mode 100644 index 0000000000..0c2e141474 --- /dev/null +++ b/plugins/nprogress.html @@ -0,0 +1,47 @@ + + + + + + + + + nprogress | VuePress Ecosystem + + + + + +

    nprogress Plugin

    @vuepress/plugin-nprogress

    Integrate nprogressopen in new window into VuePress, which can provide a progress bar when navigating to another page.

    This plugin has been integrated into the default theme.

    Usage

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

    Styles

    You can customize the style of the progress bar via CSS variables:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    + + + diff --git a/plugins/palette.html b/plugins/palette.html new file mode 100644 index 0000000000..747d0d555f --- /dev/null +++ b/plugins/palette.html @@ -0,0 +1,75 @@ + + + + + + + + + palette | VuePress Ecosystem + + + + + +

    palette

    @vuepress/plugin-palette

    Provide palette support for your theme.

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

    For theme authors, this plugin will help you to provide styles customization for users.

    Usage

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

    Palette and Style

    This plugin will provide a @vuepress/plugin-palette/palette (palette file) and a @vuepress/plugin-palette/style (style file) to be imported in your theme styles.

    The palette file is used for defining style variables, so it's likely to be imported at the beginning of your theme styles. For example, users can define CSS variablesopen in new window, SASS variablesopen in new window, LESS variablesopen in new window or Stylus variablesopen in new window in the palette, and then you can use those variables in your theme styles.

    The style file is used for overriding the default styles or adding extra styles, so it's likely to be imported at the end of your theme styles.

    Usage

    Use this plugin in your theme, assuming you are using SASS:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    Usage of Palette

    Import the plugin's palette file where your theme needs to use the corresponding variables, such as in the Layout.vue file:

    <template>
    +  <h1 class="palette-title">Hello, Palette!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* import variables from the plugin's palette file */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* set default value for variables */
    +$color: red !default;
    +
    +/* use variables in your styles */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    Then users can customize variables in .vuepress/styles/palette.scss:

    $color: green;
    +

    Usage of Style

    Import the plugin's style file after your theme's styles, for example, in the clientConfigFile:

    // import your theme's style file
    +import 'path/to/your/theme/style'
    +// import the plugin's style file
    +import '@vuepress/plugin-palette/style'
    +

    Then users can add extra styles in .vuepress/styles/index.scss and override the default styles of your theme:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    Options

    preset

    • Type: 'css' | 'sass' | 'less' | 'stylus'

    • Default: 'css'

    • Details:

      Set preset for other options.

      If you don't need advanced customization of the plugin, it's recommended to only set this option and omit other options.

    userPaletteFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • Details:

      File path of the user palette file, relative to source directory.

      The default value depends on the preset option.

      The file is where users define style variables, and it's recommended to keep the default file path as a convention.

    tempPaletteFile

    • Type: string

    • Default:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • Details:

      File path of the generated palette temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the palette file via '@vuepress/plugin-palette/palette' alias, so you don't need to change this option in most cases.

    userStyleFile

    • Type: string

    • Default:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • Details:

      File path of the user style file, relative to source directory.

      The default value depends on the preset option.

      The file is where users override default styles or add extra styles, and it's recommended to keep the default file path as a convention.

    tempStyleFile

    • Type: string

    • Default:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • Details:

      File path of the generated style temp file, relative to temp directory.

      The default value depends on the preset option.

      You should import the style file via '@vuepress/plugin-palette/style' alias, so you don't need to change this option in most cases.

    importCode

    • Type: (filePath: string) => string

    • Default:

      • css: (filePath) => `@import '${filePath}';\n`
      • sass: (filePath) => `@forward 'file:///${filePath}';\n`
      • less: (filePath) => `@import '${filePath}';\n`
      • stylus: (filePath) => `@require '${filePath}';\n`
    • Details:

      Function to generate import code.

      The default value depends on the preset option.

      This option is used for generating tempPaletteFile and tempStyleFile, and you don't need to change this option in most cases.

    + + + diff --git a/plugins/prismjs.html b/plugins/prismjs.html new file mode 100644 index 0000000000..3f7c025127 --- /dev/null +++ b/plugins/prismjs.html @@ -0,0 +1,47 @@ + + + + + + + + + prismjs | VuePress Ecosystem + + + + + +

    prismjs

    @vuepress/plugin-prismjs

    This plugin will enable syntax highlighting for markdown code fence with Prism.jsopen in new window.

    This plugin has been integrated into the default theme.

    Notice that this plugin would only tokenize the code fence without adding styles. When using it with a custom theme, you may need to choose and import Prism.js style theme yourself.

    Usage

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

    Options

    preloadLanguages

    • Type: string[]

    • Default: ['markdown', 'jsdoc', 'yaml']

    • Details:

      Languages to preload.

      By default, languages will be loaded on demand when parsing markdown files.

      However, Prism.js has some potential issuesopen in new window about loading languages dynamically. To avoid them, you can preload languages via this option.

    + + + diff --git a/plugins/pwa-popup.html b/plugins/pwa-popup.html new file mode 100644 index 0000000000..956d06e9bf --- /dev/null +++ b/plugins/pwa-popup.html @@ -0,0 +1,76 @@ + + + + + + + + + pwa-popup | VuePress Ecosystem + + + + + +

    pwa-popup

    @vuepress/plugin-pwa-popup

    Provide a popup component for users to activate the new PWA service worker manually.

    This plugin must be used together with pwa plugin, and the skipWaiting option must not be set to true.

    When the new service worker is ready, a popup will appear in the right bottom of the page to ask users to activate the waiting service worker.

    Usage

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

    Options

    locales

    • Type: Record<string, { message: string, buttonText: string }>

    • Details:

      The messages of the popup in different locales.

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

    • Example:

    export default {
    +  plugins: [
    +    pwaPlugin(),
    +    pwaPopupPlugin({
    +      locales: {
    +        '/': {
    +          message: 'New content is available.',
    +          buttonText: 'Refresh',
    +        },
    +        '/zh/': {
    +          message: '发现新内容可用',
    +          buttonText: '刷新',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    Styles

    You can customize the style of the popup via CSS variables:

    :root {
    +  --pwa-popup-z-index: 10;
    +  --pwa-popup-text-color: #2c3e50;
    +  --pwa-popup-bg-color: #ffffff;
    +  --pwa-popup-border-color: #3eaf7c;
    +  --pwa-popup-shadow: 0 4px 16px var(--pwa-popup-border-color);
    +  --pwa-popup-btn-text-color: #ffffff;
    +  --pwa-popup-btn-bg-color: #3eaf7c;
    +  --pwa-popup-btn-hover-bg-color: #4abf8a;
    +}
    +
    + + + diff --git a/plugins/pwa.html b/plugins/pwa.html new file mode 100644 index 0000000000..c0367f086f --- /dev/null +++ b/plugins/pwa.html @@ -0,0 +1,104 @@ + + + + + + + + + pwa | VuePress Ecosystem + + + + + +

    pwa

    @vuepress/plugin-pwa

    Make your VuePress site a Progressive Web Application (PWA)open in new window.

    This plugin uses workbox-buildopen in new window to generate service worker file, and uses register-service-workeropen in new window to register service worker.

    WARNING

    If you enabled this plugin once and you want to disable it, you might need `@vuepress/plugin-remove-pwa to remove the existing service worker.

    Usage

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

    Web App Manifests

    To make your website fully compliant with PWA, you need to create a Web app manifestsopen in new window file and set the icons, colors, etc. for your PWA.

    You need to put your manifest file and icons into the public files directory. In the following example, we assume that you are using the default public directory .vuepress/public.

    1. Create manifest file

    Typically .vuepress/public/manifest.webmanifest:

    {
    +  "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"
    +    }
    +  ]
    +}
    +
    1. Generate PWA icons

    To make your PWA more accessible, you need to generate some icons, and put them inside the public directory.

    Make sure the path of icons matches the icons field in your manifest file:

    • .vuepress/public/images/icons/android-chrome-192x192.png
    • .vuepress/public/images/icons/android-chrome-384x384.png

    TIP

    Some tools can help to do that. For example, Favicon Generatoropen in new window would help you to generate icons together with a sample manifest file.

    1. Set tags in head

    You also need to set some tags via head option to deploy the manifestopen in new window:

    export default {
    +  head: [
    +    ['link', { rel: 'manifest', href: '/manifest.webmanifest' }],
    +    ['meta', { name: 'theme-color', content: '#3eaf7c' }],
    +    // ...other tags
    +  ],
    +}
    +

    Options

    This plugin accepts all parameters of workbox-build's generateSW methodopen in new window in its options, except globDirectory and swDest.

    For example, you can set skipWaiting: true to auto activate the new service worker once it is ready:

    export default {
    +  plugins: [
    +    pwaPlugin({
    +      skipWaiting: true,
    +    }),
    +  ],
    +}
    +

    But if you omit skipWaiting or set it to false, you have to activate the new service worker manually:

    • For users, you can use our pwa-popup plugin together.
    • For developers, you can use our composition API to take control of the service worker behavior.

    serviceWorkerFilename

    • Type: string

    • Default: 'service-worker.js'

    • Details:

      File path of the generated service worker file, which is relative to the dest directory.

      The service worker file will only be generated in build mode.

    Composition API

    usePwaEvent

    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker is active.')
    +    })
    +  },
    +}
    +

    useSkipWaiting

    • Parameters:
    ParameterTypeDescription
    registrationServiceWorkerRegistrationThe registration of the service worker you want activate
    import { usePwaEvent, useSkipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('updated', (registration) => {
    +      console.log('The waiting service worker is available.')
    +      // activate the waiting service worker
    +      useSkipWaiting(registration)
    +    })
    +  },
    +}
    +
    + + + diff --git a/plugins/reading-time.html b/plugins/reading-time.html new file mode 100644 index 0000000000..42ed9b0308 --- /dev/null +++ b/plugins/reading-time.html @@ -0,0 +1,131 @@ + + + + + + + + + reading-time | VuePress Ecosystem + + + + + +

    reading-time

    @vuepress/plugin-reading-time

    This plugin will generate word count and estimated reading time for each page.

    Usage

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

    The plugin will inject reading time information into the readingTime of the page data, where:

    • readingTime.minutes: estimated reading time (minutes) number
    • readingTime.words: word count number

    Getting data on Node Side

    For any page, you can get estimated reading time and word count from page.data.readingTime:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    You can access it for further processing in the extendsPage lifecycle and other lifecycle:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    Getting data on Client Side

    You can import useReadingTimeData and useReadingTimeLocale from @vuepress/plugin-reading-time/client to get the reading time data and locale data of the current page:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 minute", words: "100 words" }
    +</script>
    +

    Options

    wordPerMinute

    • Type: number
    • Default: 300
    • Details: Reading speed (words per minute)

    locales

    • Type: ReadingTimeLocaleConfig

      interface ReadingTimeLocaleData {
      +  /**
      +   * Word template, `$word` will be automatically replaced by actual words
      +   */
      +  word: string
      +
      +  /**
      +   * Text for less than one minute
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * Time template
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimeLocaleConfig {
      +  [localePath: string]: ReadingTimeLocaleData
      +}
      +
    • Required: No

    • Details:

      Locales config for reading-time plugin.

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

    Client API

    You can import and use these APIs from @vuepress/plugin-reading-time/client:

    These APIs won't throw even you disable the plugin.

    useReadingTimeData

    interface ReadingTime {
    +  /** Expect reading time in minute unit */
    +  minutes: number
    +  /** Words count of content */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    null is returned when the plugin is disabled.

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** Expect reading time text in locale */
    +  time: string
    +  /** Word count text in locale */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    Advanced Usage

    This plugin is targeting plugin and theme developers mostly, so we provide a "Use API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    Why you should use "Use API"

    1. When you register a plugin multiple times, vuepress will gives you warning about that telling you only the first one takes effect. The useReadingTimePlugin automatically detects if the plugin is registered and avoid registering multiple times.
    2. If you access reading time data in extendsPage lifecycle, then @vuepress/plugin-reading-time must be called before your theme or plugin, otherwise you will get undefined for page.data.readingTime. The useReadingTimePlugin ensures that @vuepress/plugin-reading-time is called before your theme or plugin.

    We also provides a removeReadingTimePlugin api to remove the plugin.You can use this to ensure your call take effect or clear the plugin:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // this removes any existing reading time plugin at this time
    +  removeReadingTimePlugin(app)
    +
    +  // so this will take effect even if there is a reading time plugin registered before
    +  useReadingTimePlugin(app, {
    +    // your options
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    + + + diff --git a/plugins/redirect.html b/plugins/redirect.html new file mode 100644 index 0000000000..701801853b --- /dev/null +++ b/plugins/redirect.html @@ -0,0 +1,107 @@ + + + + + + + + + redirect | VuePress Ecosystem + + + + + +

    redirect

    @vuepress/plugin-redirect

    This plugin can automatically handle redirects for your site.

    Usage

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

    Control Page Redirection

    If you change the address of an existing page, you can use the redirectFrom option in Frontmatter to redirect to the address of this page, which ensures that users are redirected to the new address when they visit the old link.

    If you need to redirect an existing page to a new page, you can use the redirectTo option in Frontmatter to set the address to redirect to. This way the page will redirect to the new address when accessed.

    You can also set config with a redirect map in plugin options, see config for more details.

    Auto Locales

    The plugin can automatically redirect non-multilingual links to the multilingual pages the user needs based on the user's language preference.

    To achieve this, you need to leave the default language directory (/) blank and set autoLocale: true in plugin options. The plugin will automatically redirect to the correct page according to the user's language.

    I.E.: you need to set the following directory structure:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    And set locales in theme options with:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    So when a user accesses / or /page.html, they are automatically redirected to /en/ /en/page.html and /en/ /en/page.html based on current browser language.

    Customizing fallback behavior

    Sometimes, users may add more than one language to the system settings. By default, when a site supports a preferred language, but the page not exists for the preferred language, the plugin attempts to match the alternate language set by the user.

    If you don't need to fall back to the user's alternate language, but directly match the user's preferred language, set localeFallback: false in the plugin options.

    Customizing missing behavior

    Sometimes, when a user visits a page, the document does not yet contain the language version the user needs (a common case is that the current page has not been localized in the relevant language), so the plugin needs to perform a default action, which you can customize by defaultBehavior in the plugin options:

    • "defaultLocale": Redirect to default language or first available language page (default behavior)
    • "homepage": redirect to the home page in the current language (only available if the document contains the user's language)
    • "404": Redirect to page 404 in current language (only available if the document contains the user's language)

    Customizing default locale path

    You can customize the default locale path by setting defaultLocale in the plugin options. By default, the plugin uses the first locale key in locales as the default language.

    Automatically switch languages

    The plugin supports automatically switching the link to the multilingual page that the user needs according to the user's language preference when opening a multilingual document. In order to achieve this, you need to set switchLocale in the plugin options, which can be the following two values:

    • direct: switch directly to the user language preference page without asking
    • modal: When the user's language preference is different from the current page language, show a modal asking whether to switch language

    Customizing Locale Settings

    By default, the plugin generates a locale setting by reading locale path and lang from the site's locales option. Sometimes, you may want multiple languages to hit the same path, in which case you should set localeConfig in plugin options.

    For example, you might want all English users to match to /en/ and Chinese Traditional users to /zh/, then you can set:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    Redirecting Sites

    Sometimes you may change base or use new domain for your site, so you may want the original site automatically redirects to the new one.

    To solve this, the plugin provide vp-redirect cli.

    Usage:
    +  $ vp-redirect generate [sourceDir]
    +
    +Options:
    +  --hostname <hostname>  Hostname to redirect to (E.g.: https://new.example.com/) (default: /)
    +  -c, --config <config>  Set path to config file
    +  -o, --output <output>  Set the output directory (default: .vuepress/redirect)
    +  --cache <cache>        Set the directory of the cache files
    +  -t, --temp <temp>      Set the directory of the temporary files
    +  --clean-cache          Clean the cache files before generation
    +  --clean-temp           Clean the temporary files before generation
    +  -h, --help             Display this message
    +

    You need to pass in VuePress project source dir and also set the hostname option. The redirect helper cli will initialize your VuePress project to get pages, then generate and output the redirect html files to the output directory.

    By default, the plugin will output to .vuepress/redirect directory under source directory. And you should upload it to your original site to provide redirection.

    Plugin Options

    config

    • Type: Record<string, string> | ((app: App) => Record<string, string>)

    • Details: Redirect map.

    • Example:

      When base is set to /base/:

      • redirect /base/foo.html to /base/bar.html
      • /base/baz.html to https://example.com/qux.html.
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      Redirect post folder to posts folder:

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\/posts\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    • Type: boolean
    • Default: false
    • Details: Whether enable locales redirection.

    switchLocale

    • Type: "direct" | "modal" | false

    • Default: false

    • Details:

      Whether switch to a new locale based on user preference.

      • "direct": redirect to the new locale directly without asking
      • "modal": show a modal to let user choose whether to switch to the new locale

    localeConfig

    • Type: Record<string, string | string[]>

    • Details: Locale language config

    localeFallback

    • Type: boolean
    • Default: true
    • Details: Whether fallback to other locales user defined

    defaultBehavior

    • Type: "defaultLocale" | "homepage" | "404"
    • Default: "defaultLocale"
    • Details: Behavior when a locale version is not available for current link.

    defaultLocale

    • Type: string
    • Default: the first locale
    • Details: Default locale path.

    Frontmatter options

    redirectFrom

    • Type: string | string[]
    • Details: The link which this page redirects from.

    redirectTo

    • Type: string
    • Details: The link which this page redirects to.
    + + + diff --git a/plugins/register-components.html b/plugins/register-components.html new file mode 100644 index 0000000000..a9278b630a --- /dev/null +++ b/plugins/register-components.html @@ -0,0 +1,85 @@ + + + + + + + + + register-components | VuePress Ecosystem + + + + + +

    register-components

    @vuepress/plugin-register-components

    Register Vue components from component files or directory automatically.

    Usage

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

    Options

    components

    • Type: Record<string, string>

    • Default: {}

    • Details:

      An object that defines name of components and their corresponding file path.

      The key will be used as the component name, and the value is an absolute path of the component file.

      If the component name from this option conflicts with componentsDir option, this option will have a higher priority.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • Type: string | null

    • Default: null

    • Details:

      Absolute path to the components directory.

      Files in this directory which are matched with componentsPatterns will be registered as Vue components automatically.

    • Example:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    Components directory:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    Components will be registered like this:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    getComponentName

    • Type: (filename: string) => string

    • Default: (filename) => path.trimExt(filename.replace(/\/|\\/g, '-'))

    • Details:

      A function to get component name from the filename.

      It will only take effect on the files in the componentsDir which are matched with the componentsPatterns.

      Notice that the filename is a filepath relative to componentsDir.

    + + + diff --git a/plugins/remove-pwa.html b/plugins/remove-pwa.html new file mode 100644 index 0000000000..9db8d8de54 --- /dev/null +++ b/plugins/remove-pwa.html @@ -0,0 +1,47 @@ + + + + + + + + + remove-pwa | VuePress Ecosystem + + + + + +

    remove-pwa

    @vuepress/plugin-remove-pwa

    This plugin removes any related service worker from your VuePress site, so that users can still get updates if you removed any PWA plugin after enabling it.

    Why this plugin is needed if you used PWA plugin once?

    PWA plugins like @vuepress/plugin-pwa register service worker to your site, which will cache your site and make it available offline.

    However, if you remove pwa plugin, the old service worker will still be there, but they can never get an update because they can never found a new service worker to update to. So users will stay with the old version of your site.

    To solve this problem:

    1. A new service worker with empty contents shall be generated in the original place.
    2. The new service worker shall attempt to remove contents that old service worker cached, then it should unregister itself.

    Usage

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

    Config

    cachePrefix

    • Type: string
    • Default: 'workbox'
    • Details: The cache prefix for the service worker.

    swLocation

    • Type: string
    • Default: 'service-worker.js'
    • Details: The location of the old service worker.
    + + + diff --git a/plugins/rtl.html b/plugins/rtl.html new file mode 100644 index 0000000000..c7bcc8dbe8 --- /dev/null +++ b/plugins/rtl.html @@ -0,0 +1,53 @@ + + + + + + + + + rtl | VuePress Ecosystem + + + + + +

    rtl

    @vuepress/plugin-rtl

    This plugin will set direction to rtl on configured locales.

    Usage

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

    Options

    locales

    • Type: string[]
    • Default: ['/']
    • Details: Locale path to enable rtl.

    selector

    • Type: SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • Default: { 'html': { dir: 'rtl' } }

    • Details:

      Selector to enable rtl.

      The default settings mean that the dir attribute of the html element will be set to rtl in rtl locales.

    + + + diff --git a/plugins/search.html b/plugins/search.html new file mode 100644 index 0000000000..857c3f808f --- /dev/null +++ b/plugins/search.html @@ -0,0 +1,111 @@ + + + + + + + + + search | VuePress Ecosystem + + + + + +

    search

    @vuepress/plugin-search

    Provide local search to your documentation site.

    TIP

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

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

    Usage

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

    Local Search Index

    This plugin will generate search index from your pages locally, and load the search index file when users enter your site. In other words, this is a lightweight built-in search which does not require any external requests.

    However, when your site has a large number of pages, the size of search index file would be very large, which could slow down the page loading speed. In this case, we recommend you to use a more professional solution - docsearch.

    Options

    locales

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

    • Details:

      The text of the search box in different locales.

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

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    hotKeys

    • Type: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of `event.key` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press `event.altKey` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press `event.ctrlKey` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press `event.shiftKey` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    • Default: ['s', '/']

    • Details:

      Specify the event.keyopen in new window of the hotkeys.

      When hotkeys are pressed, the search box input will be focused.

      Set to an empty array to disable hotkeys.

    maxSuggestions

    • Type: number

    • Default: 5

    • Details:

      Specify the maximum number of search results.

    isSearchable

    • Type: (page: Page) => boolean

    • Default: () => true

    • Details:

      A function to determine whether a page should be included in the search index.

      • Return true to include the page.
      • Return false to exclude the page.
    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // exclude the homepage
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

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

    • Default: () => []

    • Details:

      A function to add extra fields to the search index of a page.

      By default, this plugin will use page title and headers as the search index. This option could help you to add more searchable fields.

    • Example:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // allow searching the `tags` frontmatter
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    Styles

    You can customize the style of the search box via CSS variables:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    Components

    • Details:

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

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

    TIP

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

    + + + diff --git a/plugins/seo/config.html b/plugins/seo/config.html new file mode 100644 index 0000000000..3ad640ac80 --- /dev/null +++ b/plugins/seo/config.html @@ -0,0 +1,81 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    hostname

    • Type: string

    • Required: Yes

    • Details:

      Deploy hostname.

    author

    • Type: Author

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

    • Details:

      Default author.

    autoDescription

    • Type: boolean

    • Default: true

    • Details:

      Whether generate description automatically

    canonical

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

    • Details:

      Canonical link

    fallBackImage

    • Type: string

    • Details:

      Fallback Image link when no image are found

    restrictions

    • Type: string

    • Details:

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

    twitterID

    • Type: string

    • Details:

      Fill in your twitter username.

    isArticle

    • Type: (page: Page) => boolean

    • Details:

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

    ogp

    • Type:

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

    • Details:

      Custom OPG Generator.

      You can use this options to edit OGP tags.

    jsonLd

    • Type:

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

    • Details:

      Custom JSON-LD Generator.

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

    customHead

    • Type:

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

    • Details:

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

    + + + diff --git a/plugins/seo/guide.html b/plugins/seo/guide.html new file mode 100644 index 0000000000..67b67b701d --- /dev/null +++ b/plugins/seo/guide.html @@ -0,0 +1,73 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    This plugin will make your site fully support Open Content Protocol OGPopen in new window and JSON-LD 1.1open in new window to enhance the SEO of the site.

    Out of Box

    The plugin works out of the box. Without any config, it will extract information from the page content as much as possible to complete the necessary tags required by OGP and JSON-LD.

    By default, the plugin will read the site config and page frontmatter to automatically generate tags as much as possible. Such as site name, page title, page type, writing date, last update date, and article tags are all automatically generated.

    The following are the <meta> tags and their values that will be injected into <head> by default:

    Default OGP Generation

    The following are the <meta> tags and their value injected into <head> by default to satisfy OGP:

    Meta NameValue
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || auto generated (when autoDescription is true in plugin options)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image ||first image in page || fallbackImage in plugin options
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternateOther languages in siteData.locales
    twitter:card"summary_large_image" (only available when image found)
    twitter:image:altpage.title (only available when image found)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    Default JSON-LD Generation

    Property NameValue
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    imageimage in page || options.hostname + page.frontmatter.image || siteFavIcon in plugin options
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    Setting Tags Directly

    You can configure the head option in the page's frontmatter to add specific tags to the page <head> to enhance SEO. For example:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    Will automatically inject <meta name="keywords" content="SEO plugin" />.

    Customize Generation

    The plugin also gives you full control over the build logic.

    Page Type

    For most pages, there are basically only two types: articles and website, so the plugin provides the isArticle option to allow you to provide logic for identifying articles.

    The option accepts a function in the format (page: Page) => boolean, by default all non-home pages generated from Markdown files are treated as articles.

    TIP

    If a page does fit into the "unpopular" genre like books, music, etc., you can handle them by setting the three options below.

    OGP

    You can use the plugin options ogp to pass in a function to modify the default OGP object to your needs and return it.

    function ogp(
    +  /** OGP Object inferred by plugin */
    +  ogp: SeoContent,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +

    For detailed parameter structure, see Config.

    For example, if you are using a third-party theme and set a banner in frontmatter for each article according to the theme requirements, then you can pass in the following ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    Like OGP, you can use the plugin options jsonLd to pass in a function to modify the default JSON-LD object to your needs and return it.

    function jsonLd(
    +  /** JSON-LD Object inferred by plugin */
    +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): ArticleSchema | BlogPostingSchema | WebPageSchema
    +

    If you are deploying your content to different sites, or same content under different URLs, you may need to set canonical option to provide a "Canonical Link" for your page. You can either set a string which will be appended before page route link, or adding a custom function (page: Page) => string | null to return a canonical link if necessary.

    Example

    If your sites are deployed under docs directory in example.com, but available in:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (primary)

    To let search engine results always be the primary choice, you may need to set canonical to https://www.example.com/docs/, so that search engine will know that the fourth URL is preferred to be indexed.

    Customize head Tags

    Sometimes you may need to fit other protocols or provide the corresponding SEO tags in the format provided by other search engines. In this case, you can use the customHead option, whose type is:

    function customHead(
    +  /** Head tag config */
    +  head: HeadConfig[],
    +  /** Page Object */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): void
    +

    You should modify the head array in this function directly.

    SEO Introduction

    Search engine optimization (SEO) is the process of improving the quality and quantity of site traffic to a site or a web page from search engines. SEO targets unpaid traffic (known as "natural" or "organic" results) rather than direct traffic or paid traffic. Unpaid traffic may originate from different kinds of searches, including image search, video search, academic search, news search, and industry-specific vertical search engines.

    As an internet marketing strategy, SEO considers how search engines work, the computer-programmed algorithms that dictate search engine behavior, what people search for, the actual search terms or keywords typed into search engines, and which search engines are preferred by their targeted audience. SEO is performed because a site will receive more visitors from a search engine when sites rank higher on the search engine results page (SERP). These visitors can then potentially be converted into customers.

    You can use Google Rich Media Structure Test Toolopen in new window to test this site.

    + + + diff --git a/plugins/seo/index.html b/plugins/seo/index.html new file mode 100644 index 0000000000..95d6ae15ba --- /dev/null +++ b/plugins/seo/index.html @@ -0,0 +1,47 @@ + + + + + + + + + seo | VuePress Ecosystem + + + + + +

    seo

    @vuepress/plugin-seo

    Usage

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/shiki.html b/plugins/shiki.html new file mode 100644 index 0000000000..64b45c2554 --- /dev/null +++ b/plugins/shiki.html @@ -0,0 +1,48 @@ + + + + + + + + + shiki | VuePress Ecosystem + + + + + +

    shiki

    @vuepress/plugin-shiki

    This plugin will enable syntax highlighting for markdown code fence with Shikiopen in new window (Shikijiopen in new window).

    TIP

    Shikiopen in new window is the syntax highlighter being used by VSCode. It has higher fidelity, but it could be slower than Prism.jsopen in new window, especially when you have a lot of code blocks.

    You could consider disabling this plugin in dev mode to get better development experience.

    Usage

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // options
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    Options

    langs

    • Type: ShikiLang[]

    • Details:

      Languages of code blocks to be parsed by shikiji.

      This option will be forwarded to getHighlighter() method of shikiji.

      You need to provide the languages list you are using explicitly, otherwise shikiji won't load any languages.

    • Also see:

    theme

    themes

    • Type: Record<'dark' | 'light', ShikiTheme>

    • Details:

      Dark / Light Dual themes of shikiji.

      This option will be forwarded to codeToHtml() method of shikiji.

    • Also see:

    + + + diff --git a/plugins/sitemap/config.html b/plugins/sitemap/config.html new file mode 100644 index 0000000000..a04bd06704 --- /dev/null +++ b/plugins/sitemap/config.html @@ -0,0 +1,37 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    hostname

    • Type: string

    • Required: Yes

    • Details:

      The domain name where the current site is deployed, the plugin needs this option to work.

    extraUrls

    • Type: string[]

    • Details:

      Extra link to be included.

      If you have some links not including in VuePress Router (normally in public directory or generated by other tools directly), you may need this option.

    • Example: ['/about.html', '/api/']

    excludePaths

    • Type: string[]

    • Default: ['/404.html']

    • Details:

      Urls excluding from sitemap, starting with absolute path.

      By default, all the urls generated by VuePress (excluding 404 page) will be added into sitemap.

    devServer

    • Type: boolean

    • Default: false

    • Details:

      Whether enabled in devServer.

    TIP

    For performance reasons, we do not provide hot reload. Reboot your devServer to sync your changes.

    devHostname

    • Type: string

    • Default: "http://localhost:${port}"

    • Details:

      Hostname to use in devServer

    sitemapFilename

    • Type: string

    • Default value: "sitemap.xml"

    • Details:

      The output filename, relative to output directory.

    sitemapXSLFilename

    • Type: string

    • Default value: "sitemap.xsl"

    • Details:

      Output xsl filename, relative to dest folder.

    sitemapXSLTemplate

    • Type: string

    • Default value: "@vuepress/plugin-sitemap/templates/sitemap.xsl"

    • Details:

      XSL content used as template.

    changefreq

    • Type: "always" | "hourly" | "daily" | "weekly" |"monthly" | "yearly" | "never"

    • Default value: "daily"

    • Details:

      Page default update frequency, will be overridden by sitemap.changefreq in Frontmatter.

    priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, from 0 to 1.

    modifyTimeGetter

    • Type: (page: Page, app: App) => string

    • Details:

      Last modify time getter. By default, the plugin will use the timestamp generated by git plugin.

    + + + diff --git a/plugins/sitemap/frontmatter.html b/plugins/sitemap/frontmatter.html new file mode 100644 index 0000000000..5e4e5b7f74 --- /dev/null +++ b/plugins/sitemap/frontmatter.html @@ -0,0 +1,37 @@ + + + + + + + + + Frontmatter | VuePress Ecosystem + + + + + +

    Frontmatter

    sitemap

    • Type: SitemapFrontmatterOptions | false

    • Details:

      false means exclude the page from sitemap.

    sitemap.changefreq

    • Type: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    • Default: "daily"

    • Details:

      Page default update frequency. This will override changefreq in Plugin Options.

    sitemap.priority

    • Type: number

    • Default: 0.5

    • Details:

      Page priority, range from 0 to 1.

    + + + diff --git a/plugins/sitemap/guide.html b/plugins/sitemap/guide.html new file mode 100644 index 0000000000..e2447a5464 --- /dev/null +++ b/plugins/sitemap/guide.html @@ -0,0 +1,45 @@ + + + + + + + + + Guide | VuePress Ecosystem + + + + + +

    Guide

    This plugin will automatically generate a Sitemap for your site. To let this plugin work, you need to pass the deployed domain name to the hostname option of the plugin. If you want to preview in devServer, set devServer options.

    The plugin will automatically generate the last update time of the page based on the Git timestamp of the page, and will also declare the alternative links of the page in other languages according to the locales' config.

    By default, all site links except 404 page will be added to the Sitemap.

    To add other pages to the Sitemap outside the VuePress project page, please turn them into an array and pass to the extraUrls plugin option.

    If you don't want certain pages to appear in the sitemap, you can turn their paths into an array and pass to the excludePaths plugin option, or set sitemap to false in the frontmatter of the corresponding page.

    Output Location

    You can also control the output link through the sitemapFilename option of the plugin, the link is relative to output directory. By default, the plugin will use sitemap.xml.

    Change Frequency

    The default update cycle of the page is daily (every day). To modify the entire page cycle, please set changefreq in the plugin options. You can also set sitemap.changefreq in the frontmatter of the page. Note that page has a higher priority.

    The legal frequencies are:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    Priority

    You can set priority in the plugin to provide a default value. At the same time you can set the priority for each page through sitemap.priority in frontmatter. Acceptable values are floating point numbers from 0 to 1.

    Modify Time

    You can use option modifyTimeGetter to return a time in ISO string format, which is generated by the Git plugin by default.

    The following is an example based on the last modification time of a file.

    // Based on file last modified time
    +({
    +   modifyTimeGetter: (page, app) =>
    +     fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap Intro

    Sitemaps provide SEO (Search Engine Optimization):

    • Provide search engine spiders with links of the entire site;
    • Provide some links for search engine spiders to dynamic pages or pages that are difficult to reach by other methods;
    • If a visitor attempts to access a URL that does not exist within the site's domain, the visitor will be directed to a "file not found" error page, and the sitemap can be used as a navigation page.

    A sitemap enhances SEO by making all pages findable.

    Most search engines only follow a limited number of links within a page, so when the site is very large, a sitemap becomes essential to make everything on the site accessible to search engines and visitors.

    Sitemaps is a protocol for site administrators to publish pages that can be crawled on a site to search engine spiders. The content of sitemap files must follow the definition in XML format. Each URL can contain the update period and last update time, the priority of the URL across the site. This allows search engines to crawl site content better and more efficiently.

    Together with robots.txt

    Sitemap is basically used by search engines, when using this plugin, you'd better ensure that you have a valid robots.txt in the .vuepress/public directory to allow search engines spiders to visit your site. The simplest robots.txt is as follows (allow all search engines to access all paths)

    User-agent: *
    +
    +Allow: /
    +
    + + + diff --git a/plugins/sitemap/index.html b/plugins/sitemap/index.html new file mode 100644 index 0000000000..a765cea787 --- /dev/null +++ b/plugins/sitemap/index.html @@ -0,0 +1,47 @@ + + + + + + + + + sitemap | VuePress Ecosystem + + + + + +

    sitemap

    @vuepress/plugin-sitemap

    Usage

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/plugins/theme-data.html b/plugins/theme-data.html new file mode 100644 index 0000000000..5761b321f4 --- /dev/null +++ b/plugins/theme-data.html @@ -0,0 +1,87 @@ + + + + + + + + + theme-data | VuePress Ecosystem + + + + + +

    theme-data

    @vuepress/plugin-theme-data

    Provide client data for your theme, with VuePress i18n support.

    This plugin is mainly used to develop themes, and has been integrated into the default theme. You won't need to use it directly in most cases.

    For theme authors, this plugin will help you to use the same i18n mechanism as VuePress and the default theme. But if you don't want to provide i18n support, or you want to implement in your own way, you don't need this plugin.

    Usage

    npm i -D @vuepress/plugin-theme-data@next
    +
    import { themeDataPlugin } from '@vuepress/plugin-theme-data'
    +
    +export default {
    +  plugins: [
    +    themeDataPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Options

    themeData

    • Type: ThemeData

    • Default: {}

    • Details:

      The theme data object that you want to use in client side.

      You can provide theme data in Node side via this option, and use it in client side via useThemeData and useThemeLocaleData.

    • Example:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    WARNING

    The theme data object will be processed by JSON.stringify() before forwarding to client side, so you should ensure that you are providing a JSON-friendly object.

    Composition API

    useThemeData

    • Details:

      Returns the theme data ref object.

      The value is provided by themeData option.

    • Example:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • Details:

      Returns the theme data ref object in current locale.

      The properties of current locale has been merged into the root-level properties.

    • Example:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    + + + diff --git a/plugins/toc.html b/plugins/toc.html new file mode 100644 index 0000000000..25184601b7 --- /dev/null +++ b/plugins/toc.html @@ -0,0 +1,110 @@ + + + + + + + + + toc | VuePress Ecosystem + + + + + +

    toc

    @vuepress/plugin-toc

    This plugin will provide a table-of-contents (TOC) component.

    Usage

    npm i -D @vuepress/plugin-toc@next
    +
    import { tocPlugin } from '@vuepress/plugin-toc'
    +
    +export default {
    +  plugins: [
    +    tocPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Differences with Markdown TOC Syntax

    Similar to the Table of Contents Markdown Syntax, the TOC component that provided by this plugin could be used in your markdown content directly:

    <!-- markdown toc syntax -->
    +
    +[[toc]]
    +
    +<!-- vue toc component -->
    +<Toc />
    +

    Both of them can be pre-rendered correctly in build mode. However, there are some differences between them.

    The markdown syntax [[toc]] could only be used in markdown files. It is parsed by markdown-it, and the generated TOC is static content.

    The component <Toc/> could be used in both markdown files and vue files. It is loaded by vue, and the generated TOC is a vue component.

    This plugin could work together with @vuepress/plugin-active-header-links by setting the headerLinkSelector to match the linkClass option. When the page scroll to a certain header anchor, this corresponding link will be added linkActiveClass class name.

    Therefore, this plugin is more useful for theme developers.

    Options

    componentName

    • Type: string

    • Default: 'Toc'

    • Details:

      Specify the name of the TOC component.

    defaultPropsOptions

    • Type: Partial<TocPropsOptions>

    • Default: {}

    • Details:

      Override the default values of the component options prop.

    Component Props

    The TOC component also accepts props for customization.

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • Type: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • Details:

      Specify the headers array to render.

      If this prop is not specified, the headers of current page will be used.

    options

    • Type: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouterLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • Details:

      Customize the TOC component.

      If the containerTag is set to an empty string '', the <nav> container will be removed totally.

    • Example:

      The rendered TOC component with default options looks like:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouterLink class="vuepress-toc-link" to="#foo">Foo</RouterLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouterLink class="vuepress-toc-link active" to="#bar">Bar</RouterLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouterLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouterLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    + + + diff --git a/themes/default/components.html b/themes/default/components.html new file mode 100644 index 0000000000..970f2f777f --- /dev/null +++ b/themes/default/components.html @@ -0,0 +1,68 @@ + + + + + + + + + Built-in Components | VuePress Ecosystem + + + + + +

    Built-in Components

    @vuepress/theme-default

    Badge badge

    • Props:

      • type
        • Type: 'tip' | 'warning' | 'danger'
        • Default: 'tip'
      • text
        • Type: string
        • Default: ''
      • vertical
        • Type: 'top' | 'middle' | 'bottom' | undefined
        • Default: undefined
    • Example:

    Input

    - VuePress - <Badge type="tip" text="v2" vertical="top" />
    +- VuePress - <Badge type="warning" text="v2" vertical="middle" />
    +- VuePress - <Badge type="danger" text="v2" vertical="bottom" />
    +

    Output

    • VuePress - v2
    • VuePress - v2
    • VuePress - v2

    CodeGroup

    CodeGroupItem

    • Props:

      • title
        • Type: string
        • Required: true
      • active
        • Type: boolean
        • Default: false
    • Details:

      This component must be placed inside a CodeGroup component.

      Use the active prop to set the initial active item, or the first item will be activated by default.

    • Example:

    Input

    <CodeGroup>
    +  <CodeGroupItem title="pnpm">
    +
    +```bash:no-line-numbers
    +pnpm install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="yarn">
    +
    +```bash:no-line-numbers
    +yarn install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="npm" active>
    +
    +```bash:no-line-numbers
    +npm install
    +```
    +
    +  </CodeGroupItem>
    +</CodeGroup>
    +

    Output

    pnpm install
    +
    yarn install
    +
    npm install
    +

    WARNING

    You must add an empty line between the starting tag of <CodeGroupItem> and the code fence, otherwise the code fence will not be parsed correctly by Markdown.

    All content must be valid Markdown first, and then a Vue SFC.

    Learn more: Cookbook > Markdown and Vue SFCopen in new window

    Alternatively, you can use the custom containers.

    + + + diff --git a/themes/default/config.html b/themes/default/config.html new file mode 100644 index 0000000000..bb33bb452e --- /dev/null +++ b/themes/default/config.html @@ -0,0 +1,177 @@ + + + + + + + + + Config | VuePress Ecosystem + + + + + +

    Config

    Basic Config

    hostname

    • Type: string

    • Details:

      Hostname to be deployed, e.g.: https://example.com

    locales

    • Type: { [path: string]: Partial<DefaultThemeLocaleData> }

    • Default: {}

    • Details:

      Specify locales for i18n support.

      All the options inside the Locale Config section can be used in locales.

      This option will only take effect in default theme, so don't confuse with locales in Site Config.

    • Also see:

    Locale Config

    Config of this section can be used as normal config, and can also be used in the locales option.

    colorMode

    colorModeSwitch

    home

    • Type: string

    • Default: /

    • Details:

      Specify the path of the homepage.

      This will be used for:

      • the logo link of the navbar
      • the back to home link of the 404 page
    • Type: false | (NavbarItem | NavbarGroup | string)[]

    • Default: []

    • Details:

      Configuration of navbar.

      Set to false to disable navbar.

      To configure the navbar items, you can set it to a navbar array, each item of which could be a NavbarItem object, a NavbarGroup object, or a string:

      • A NavbarItem object should have a text field and a link field, could have an optional activeMatch field.
      • A NavbarGroup object should have a text field and a children field. The children field should be a navbar array, too.
      • A string should be the path to the target page file. It will be converted to a NavbarItem object, using the page title as text, and the page route path as link.
    • Example 1:

    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // NavbarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +      },
    +      // NavbarGroup
    +      {
    +        text: 'Group',
    +        children: ['/group/foo.md', '/group/bar.md'],
    +      },
    +      // string - page file path
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • Example 2:
    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // nested group - max depth is 2
    +      {
    +        text: 'Group',
    +        children: [
    +          {
    +            text: 'SubGroup',
    +            children: ['/group/sub/foo.md', '/group/sub/bar.md'],
    +          },
    +        ],
    +      },
    +      // control when should the item be active
    +      {
    +        text: 'Group 2',
    +        children: [
    +          {
    +            text: 'Always active',
    +            link: '/',
    +            // this item will always be active
    +            activeMatch: '/',
    +          },
    +          {
    +            text: 'Active on /foo/',
    +            link: '/not-foo/',
    +            // this item will be active when current route path starts with /foo/
    +            // regular expression is supported
    +            activeMatch: '^/foo/',
    +          },
    +        ],
    +      },
    +    ],
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      Specify the url of logo image.

      The logo image will be displayed at the left end of the navbar.

      Set to null to disable logo.

    • Example:

    export default {
    +  theme: defaultTheme({
    +    // public file path
    +    logo: '/hero.png',
    +    // url
    +    logo: 'https://vuejs.org/images/logo.png',
    +  }),
    +}
    +

    logoDark

    logoAlt

    • Type: null | string

    • Details:

      Specify the alt text of the logo image.

      If not specified, defaults to be the same as the site title.

    repo

    • Type: string

    • Details:

      Specify the repository url of your project.

      This will be used as the link of the repository link, which will be displayed as the last item of the navbar.

    export default {
    +  theme: defaultTheme({
    +    // If you set it in the form of `organization/repository`
    +    // we will take it as a GitHub repo
    +    repo: 'vuejs/vuepress',
    +    // You can also set it to a URL directly
    +    repo: 'https://gitlab.com/foo/bar',
    +  }),
    +}
    +
    • Type: false | 'auto' | SidebarConfigArray | SidebarConfigObject

    • Default: 'auto'

    • Details:

      Configuration of sidebar.

      You can override this global option via sidebar frontmatter in your pages.

      Set to false to disable sidebar.

      If you set it to 'auto', the sidebar will be automatically generated from the page headers.

      To configure the sidebar items manually, you can set this option to a sidebar array, each item of which could be a SidebarItem object or a string:

      • A SidebarItem object should have a text field, could have an optional link field, an optional children field and an optional collapsible field. The children field should be a sidebar array. The collapsible field controls whether the item is collapsible.
      • A string should be the path to the target page file. It will be converted to a SidebarItem object, whose text is the page title, link is the page route path, and children is automatically generated from the page headers.

      If you want to set different sidebar for different sub paths, you can set this option to a sidebar object:

      • The key should be the path prefix.
      • The value should be a sidebar array or set to 'heading' to automatically generate the sidebar from the page headers for just the corresponding path.
    • Example 1:

    export default {
    +  theme: defaultTheme({
    +    // sidebar array
    +    // all pages will use the same sidebar
    +    sidebar: [
    +      // SidebarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +        children: [
    +          // SidebarItem
    +          {
    +            text: 'github',
    +            link: 'https://github.com',
    +            children: [],
    +          },
    +          // string - page file path
    +          '/foo/bar.md',
    +        ],
    +      },
    +      // string - page file path
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • Example 2:
    export default {
    +  theme: defaultTheme({
    +    // sidebar object
    +    // pages under different sub paths will use different sidebar
    +    sidebar: {
    +      '/guide/': [
    +        {
    +          text: 'Guide',
    +          children: ['/guide/introduction.md', '/guide/getting-started.md'],
    +        },
    +      ],
    +      '/reference/': 'heading',
    +    },
    +  }),
    +}
    +
    • Example 3:
    export default {
    +  theme: defaultTheme({
    +    // collapsible sidebar
    +    sidebar: {
    +      '/reference/': [
    +        {
    +          text: 'VuePress Reference',
    +          collapsible: true,
    +          children: ['/reference/cli.md', '/reference/config.md'],
    +        },
    +        {
    +          text: 'Bundlers Reference',
    +          collapsible: true,
    +          children: [
    +            '/reference/bundler/vite.md',
    +            '/reference/bundler/webpack.md',
    +          ],
    +        },
    +      ],
    +    },
    +  }),
    +}
    +

    sidebarDepth

    • Type: number

    • Default: 2

    • Details:

      Set the maximum depth of the sidebar children which are automatically generated from the page headers.

      • Set to 0 to disable all levels of headers.
      • Set to 1 to include <h2> headers.
      • Set to 2 to include <h2> and <h3> headers.
      • ...

      The max value depends on which levels of headers you have extracted via markdown.headers.levelopen in new window.

      The default value of markdown.headers.level is [2, 3], so the default max value of sidebarDepth is 2.

      You can override this global option via sidebarDepth frontmatter in your pages.

    • Type: boolean

    • Default: true

    • Details:

      Enable the edit this page link or not.

      You can override this global option via editLink frontmatter in your pages.

    editLinkPattern

    • Type: string

    • Details:

      Specify the pattern of the edit this page link.

      This will be used for generating the edit this page link.

      If you don't set this option, the pattern will be inferred from the docsRepo option. But if your documentation repository is not hosted on a common platform, for example, GitHub, GitLab, Bitbucket, Gitee, etc., you have to set this option explicitly to make the edit this page link work.

    • Usage:

      PatternDescription
      :repoThe docs repo url, i.e. docsRepo
      :branchThe docs repo branch, i.e. docsBranch
      :pathThe path of the page source file, i.e. docsDir joins the relative path of the page file
    • Example:

    export default {
    +  theme: defaultTheme({
    +    docsRepo: 'https://gitlab.com/owner/name',
    +    docsBranch: 'master',
    +    docsDir: 'docs',
    +    editLinkPattern: ':repo/-/edit/:branch/:path',
    +  }),
    +}
    +

    The generated link will look like 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md'.

    docsRepo

    • Type: string

    • Details:

      Specify the repository url of your documentation source files.

      This will be used for generating the edit this page link.

      If you don't set this option, it will use the repo option by default. But if your documentation source files are in a different repository, you will need to set this option.

    docsBranch

    • Type: string

    • Default: 'main'

    • Details:

      Specify the repository branch of your documentation source files.

      This will be used for generating the edit this page link.

    docsDir

    • Type: string

    • Default: ''

    • Details:

      Specify the directory of your documentation source files in the repository.

      This will be used for generating the edit this page link.

    lastUpdated

    • Type: boolean

    • Default: true

    • Details:

      Enable the last updated timestamp or not.

      You can override this global option via lastUpdated frontmatter in your pages. Notice that if you have already set this option to false, this feature will be disabled totally and could not be enabled in locales nor page frontmatter.

    contributors

    • Type: boolean

    • Default: true

    • Details:

      Enable the contributors list or not.

      You can override this global option via contributors frontmatter in your pages. Notice that if you have already set this option to false, this feature will be disabled totally and could not be enabled in locales nor page frontmatter.

    + + + diff --git a/themes/default/extending.html b/themes/default/extending.html new file mode 100644 index 0000000000..5826f59fb9 --- /dev/null +++ b/themes/default/extending.html @@ -0,0 +1,102 @@ + + + + + + + + + Extending | VuePress Ecosystem + + + + + +

    Extending

    VuePress default theme is widely used by users, so it is designed to be extendable, allowing users to make their own customization with ease.

    Layout Slots

    Default theme's Layout provides some slots:

    • navbar
    • navbar-before
    • navbar-after
    • sidebar
    • sidebar-top
    • sidebar-bottom
    • page
    • page-top
    • page-bottom
    • page-content-top
    • page-content-bottom

    With the help of them, you can add or replace content easily. Here comes an example to introduce how to extend default theme with layout slots.

    Firstly, create a client config file .vuepress/client.ts:

    import { defineClientConfig } from 'vuepress/client'
    +import Layout from './layouts/Layout.vue'
    +
    +export default defineClientConfig({
    +  layouts: {
    +    Layout,
    +  },
    +})
    +

    Next, create the .vuepress/layouts/Layout.vue, and make use of the slots that provided by the Layout of default theme:

    <script setup>
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +</script>
    +
    +<template>
    +  <ParentLayout>
    +    <template #page-bottom>
    +      <div class="my-footer">This is my custom page footer</div>
    +    </template>
    +  </ParentLayout>
    +</template>
    +
    +<style lang="css">
    +.my-footer {
    +  text-align: center;
    +}
    +</style>
    +

    Then the default Layout layout has been overridden by your own local layout, which will add a custom footer to every normal pages in default theme (excluding homepage):

    extending-a-theme

    Components Replacement

    The layout slots are useful, but sometimes you might find it's not flexible enough. Default theme also provides the ability to replace a single component.

    Default theme has registered aliasopen in new window for every non-global componentsopen in new window with a @theme prefix. For example, the alias of HomeFooter.vue is @theme/HomeFooter.vue.

    Then, if you want to replace the HomeFooter.vue component, just override the alias in your config file .vuepress/config.ts:

    import { defaultTheme } from '@vuepress/theme-default'
    +import { getDirname, path } from 'vuepress/utils'
    +import { defineUserConfig } from 'vuepress'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default defineUserConfig({
    +  theme: defaultTheme(),
    +  alias: {
    +    '@theme/HomeFooter.vue': path.resolve(
    +      __dirname,
    +      './components/MyHomeFooter.vue',
    +    ),
    +  },
    +})
    +

    Developing a Child Theme

    Instead of extending the default theme directly in .vuepress/config.ts and .vuepress/client.ts, you can also develop your own theme extending the default theme:

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // override layouts in child theme's client config file
    +    // notice that you would build ts to js before publishing to npm,
    +    // so this should be the path to the js file
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // override component alias
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    + + + diff --git a/themes/default/frontmatter.html b/themes/default/frontmatter.html new file mode 100644 index 0000000000..1b85a1f78a --- /dev/null +++ b/themes/default/frontmatter.html @@ -0,0 +1,96 @@ + + + + + + + + + Frontmatter | VuePress Ecosystem + + + + + +

    Frontmatter

    @vuepress/theme-default

    All Pages

    Frontmatter in this section will take effect in all types of pages.

    externalLinkIcon

    • Type: boolean

    • Details:

      Show navbar on this page or not.

      If you disable navbar in theme config, this frontmatter will not take effect.

    • Also see:

    pageClass

    • Type: string

    • Details:

      Add extra class name to this page.

    • Example:

    ---
    +pageClass: custom-page-class
    +---
    +

    Then you can customize styles of this page in .vuepress/styles/index.scss file:

    .theme-container.custom-page-class {
    +  /* page styles */
    +}
    +

    Home Page

    Frontmatter in this section will only take effect in home pages.

    home

    • Type: boolean

    • Details:

      Specify whether the page is homepage or a normal page.

      If you don't set this frontmatter or set it to false, the page would be a normal page.

    • Example:

    ---
    +home: true
    +---
    +

    heroImage

    • Type: string

    • Details:

      Specify the url of the hero image.

    • Example:

    ---
    +# public file path
    +heroImage: /images/hero.png
    +# url
    +heroImage: https://vuejs.org/images/logo.png
    +---
    +

    heroImageDark

    heroAlt

    • Type: string

    • Details:

      Specify the alt attribute of the hero image.

      This will fallback to the heroText.

    heroHeight

    • Type: number

    • Default: 280

    • Details:

      Specify the height attribute of the hero <img> tag.

      You may need to reduce this value if the height of your hero image is less than the default value.

      Notice that the height is also constrained by CSS. This attribute is to reduce Cumulative Layout Shift (CLS)open in new window that caused by the loading of the hero image.

    heroText

    • Type: string | null

    • Details:

      Specify the the hero text.

      This will fallback to the site titleopen in new window.

      Set to null to disable hero text.

    tagline

    actions

    • Type:
    Array<{
    +  text: string
    +  link: string
    +  type?: 'primary' | 'secondary'
    +}>
    +
    • Details:

      Configuration of the action buttons.

    • Example:

    ---
    +actions:
    +  - text: Get Started
    +    link: /guide/getting-started.html
    +    type: primary
    +  - text: Introduction
    +    link: /guide/introduction.html
    +    type: secondary
    +---
    +

    features

    • Type:
    Array<{
    +  title: string
    +  details: string
    +}>
    +
    • Details:

      Configuration of the features list.

    • Example:

    ---
    +features:
    +  - title: Simplicity First
    +    details: Minimal setup with markdown-centered project structure helps you focus on writing.
    +  - title: Vue-Powered
    +    details: Enjoy the dev experience of Vue, use Vue components in markdown, and develop custom themes with Vue.
    +  - title: Performant
    +    details: VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
    +---
    +
    • Type: string

    • Details:

      Specify the content of the footer.

    footerHtml

    • Type: boolean

    • Details:

      Allow HTML in footer or not.

      If you set it to true, the footer will be treated as HTML code.

    Normal Page

    Frontmatter in this section will only take effect in normal pages.

    editLinkPattern

    lastUpdated

    contributors

    sidebarDepth

    prev

    • Type: NavLink | string

    • Details:

      Specify the link of the previous page.

      If you don't set this frontmatter, the link will be inferred from the sidebar config.

      To configure the prev link manually, you can set this frontmatter to a NavLink object or a string:

      • A NavLink object should have a text field and a link field.
      • A string should be the path to the target page file. It will be converted to a NavLink object, whose text is the page title, and link is the page route path.
    • Example:

    ---
    +# NavLink
    +prev:
    +  text: Get Started
    +  link: /guide/getting-started.html
    +
    +# NavLink - external url
    +prev:
    +  text: GitHub
    +  link: https://github.com
    +
    +# string - page file path
    +prev: /guide/getting-started.md
    +
    +# string - page file relative path
    +prev: ../../guide/getting-started.md
    +---
    +

    next

    • Type: NavLink | string

    • Details:

      Specify the link of the next page.

      If you don't set this frontmatter, the link will be inferred from the sidebar config.

      The type is the same as prev frontmatter.

    + + + diff --git a/themes/default/index.html b/themes/default/index.html new file mode 100644 index 0000000000..7423b01a3d --- /dev/null +++ b/themes/default/index.html @@ -0,0 +1,45 @@ + + + + + + + + + Usage | VuePress Ecosystem + + + + + + + + + diff --git a/themes/default/locale.html b/themes/default/locale.html new file mode 100644 index 0000000000..0cb8d93fdd --- /dev/null +++ b/themes/default/locale.html @@ -0,0 +1,57 @@ + + + + + + + + + Locale Config | VuePress Ecosystem + + + + + +

    Locale Config

    These options configure locale-related texts.

    If your site is served in a different language besides English, you should set these options per locale to provide translations.

    repoLabel

    • Type: string

    • Details:

      Specify the repository label of your project.

      This will be used as the text of the repository link, which will be displayed as the last item of the navbar.

      If you don't set this option explicitly, it will be automatically inferred from the repo option.

    selectLanguageText

    • Type: string

    • Details:

      Specify the text of the select language menu.

      The select language menu will appear next to the repository button in the navbar when you set multiple locales in your site config.

    selectLanguageAriaLabel

    • Type: string

    • Details:

      Specify the aria-label attribute of the select language menu.

      This is mainly for a11y purpose.

    selectLanguageName

    • Type: string

    • Details:

      Specify the name of the language of a locale.

      This option will only take effect inside the locales of your theme config. It will be used as the language name of the locale, which will be displayed in the select language menu.

    • Example:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • Type: null | string

    • Details:

      aria-label value for main navigation in navbar.

    • Type: null | string

    • Details:

      aria-label value for next/previous page navigation.

    editLinkText

    • Type: string

    • Default: 'Edit this page'

    • Details:

      Specify the text of the edit this page link.

    lastUpdatedText

    • Type: string

    • Default: 'Last Updated'

    • Details:

      Specify the text of the last updated timestamp label.

    contributorsText

    • Type: string

    • Default: 'Contributors'

    • Details:

      Specify the text of the contributors list label.

    tip

    • Type: string

    • Default: 'TIP'

    • Details:

      Specify the default title of the tip custom containers.

    warning

    • Type: string

    • Default: 'WARNING'

    • Details:

      Specify the default title of the warning custom containers.

    danger

    • Type: string

    • Default: 'DANGER'

    • Details:

      Specify the default title of the danger custom containers.

    notFound

    • Type: string[]

    • Default: ['Not Found']

    • Details:

      Specify the messages of the 404 page.

      The message will be randomly picked from the array when users enter the 404 page.

    backToHome

    • Type: string

    • Default: 'Back to home'

    • Details:

      Specify the text of the back to home link in the 404 page.

    openInNewWindow

    toggleColorMode

    toggleSidebar

    • Type: string

    • Default: 'toggle sidebar'

    • Details:

      Title text for sidebar toggle button.

      This is mainly for a11y purpose.

    + + + diff --git a/themes/default/markdown.html b/themes/default/markdown.html new file mode 100644 index 0000000000..ecca570392 --- /dev/null +++ b/themes/default/markdown.html @@ -0,0 +1,86 @@ + + + + + + + + + Markdown | VuePress Ecosystem + + + + + +

    Markdown

    @vuepress/theme-default

    Custom Containers

    • Usage:

      ::: <type> [title]
      +[content]
      +:::
      +

      The type is required, and the title and content are optional.

      Supported type :

    • Example 1 (default title):

    Input

    ::: tip
    +This is a tip
    +:::
    +
    +::: warning
    +This is a warning
    +:::
    +
    +::: danger
    +This is a dangerous warning
    +:::
    +
    +::: details
    +This is a details block
    +:::
    +

    Output

    TIP

    This is a tip

    WARNING

    This is a warning

    DANGER

    This is a dangerous warning

    This is a details block

    • Example 2 (custom title):

    Input

    ::: danger STOP
    +Danger zone, do not proceed
    +:::
    +
    +::: details Click me to view the code
    +
    +```ts
    +console.log('Hello, VuePress!')
    +```
    +
    +:::
    +

    Output

    STOP

    Danger zone, do not proceed

    Click me to view the code
    console.log('Hello, VuePress!')
    +
    • Example 3 (code group alias):

    Input

    :::: code-group
    +::: code-group-item FOO
    +
    +```ts
    +const foo = 'foo'
    +```
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +```ts
    +const bar = 'bar'
    +```
    +
    +:::
    +::::
    +

    Output

    const foo = 'foo'
    +
    const bar = 'bar'
    +
    + + + diff --git a/themes/default/plugin.html b/themes/default/plugin.html new file mode 100644 index 0000000000..c74ceac563 --- /dev/null +++ b/themes/default/plugin.html @@ -0,0 +1,46 @@ + + + + + + + + + Plugins Config | VuePress Ecosystem + + + + + +

    Plugins Config

    You can configure the plugins that used by default theme with themePlugins.

    Default theme is using some plugins by default. You can disable a plugin if you really do not want to use it. Make sure you understand what the plugin is for before disabling it.

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // customize theme plugins here
    +    },
    +  }),
    +}
    +

    themePlugins.backToTop

    • Type: BackToTopPluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-back-to-top or not.

      Object value is supported as plugin options.

    themePlugins.container

    themePlugins.externalLinkIcon

    themePlugins.copyCode

    • Type: CopyCodePluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-copy-code or not.

      Object value is supported as plugin options.

    themePlugins.git

    themePlugins.mediumZoom

    themePlugins.nprogress

    themePlugins.seo

    • Type: SeoPluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-seo or not.

      Object value is supported as plugin options.

    themePlugins.sitemap

    • Type: SitemapPluginOptions | boolean

    • Default: true

    • Details:

      Enable @vuepress/plugin-sitemap or not.

      Object value is supported as plugin options.

    + + + diff --git a/themes/default/styles.html b/themes/default/styles.html new file mode 100644 index 0000000000..95c7b3b89d --- /dev/null +++ b/themes/default/styles.html @@ -0,0 +1,268 @@ + + + + + + + + + Styles | VuePress Ecosystem + + + + + +

    Styles

    @vuepress/theme-default

    The default theme uses SASSopen in new window as the CSS pre-processor.

    Users can customize style variables via a palette file, and add extra styles via a style file.

    Palette File

    The path of the palette file is .vuepress/styles/palette.scss.

    You can make use of it to override predefined SASS variables of the default theme.

    Click to expand SASS variables
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style File

    The path of the style file is .vuepress/styles/index.scss.

    You can add extra styles here, or override the default styles:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    You can also make use of it to override predefined CSS variables of the default theme.

    Click to expand CSS variables
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.back-to-top {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +.pwa-popup {
    +  --pwa-popup-text-color: var(--c-text);
    +  --pwa-popup-bg-color: var(--c-bg);
    +  --pwa-popup-border-color: var(--c-brand);
    +  --pwa-popup-shadow: 0 4px 16px var(--c-brand);
    +  --pwa-popup-btn-text-color: var(--c-bg);
    +  --pwa-popup-btn-bg-color: var(--c-brand);
    +  --pwa-popup-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    Click to expand dark mode CSS variables
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    + + + diff --git a/zh/index.html b/zh/index.html new file mode 100644 index 0000000000..5c08de010b --- /dev/null +++ b/zh/index.html @@ -0,0 +1,37 @@ + + + + + + + + + 首页 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/plugins/active-header-links.html b/zh/plugins/active-header-links.html new file mode 100644 index 0000000000..7a7a322eca --- /dev/null +++ b/zh/plugins/active-header-links.html @@ -0,0 +1,47 @@ + + + + + + + + + active-header-links | VuePress 生态系统 + + + + + +

    active-header-links

    @vuepress/plugin-active-header-links

    该插件会监听页面滚动事件。当页面滚动至某个 标题锚点 后,如果存在对应的 标题链接 ,那么该插件会将路由 Hash 更改为该 标题锚点

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    使用方法

    npm i -D @vuepress/plugin-active-header-links@next
    +
    import { activeHeaderLinksPlugin } from '@vuepress/plugin-active-header-links'
    +
    +export default {
    +  plugins: [
    +    activeHeaderLinksPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    headerLinkSelector

    • 类型: string

    • 默认值: 'a.sidebar-item'

    • 详情:

      标题链接 的选择器。

      如果一个 标题锚点 没有对应的 标题链接 ,那么即使滚动到这个 标题锚点 ,该插件也不会更改路由 Hash 。

    headerAnchorSelector

    delay

    • 类型: number

    • 默认值: 200

    • 详情:

      滚动事件监听器的 Debounce 延迟。

    offset

    • 类型: number

    • 默认值: 5

    • 详情:

      即便直接点击 标题锚点 的链接, scrollTop 也可能不会完全等于 标题锚点offsetTop ,所以我们添加一个 Offset 偏移量来避免这个误差。

    + + + diff --git a/zh/plugins/back-to-top.html b/zh/plugins/back-to-top.html new file mode 100644 index 0000000000..ba2c1eabcc --- /dev/null +++ b/zh/plugins/back-to-top.html @@ -0,0 +1,51 @@ + + + + + + + + + back-to-top | VuePress 生态系统 + + + + + +

    back-to-top

    @vuepress/plugin-back-to-top

    该插件会给你的站点添加一个 返回顶部 按钮。当页面向下滚动时,该按钮会显示在页面的右下角,点击它就会滚动到页面顶部。

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-back-to-top@next
    +
    import { backToTopPlugin } from '@vuepress/plugin-back-to-top'
    +
    +export default {
    +  plugins: [backToTopPlugin()],
    +}
    +

    选项

    threshold

    • 类型:数字
    • 默认值:100
    • 详情:显示返回顶部按钮的滚动阈值距离(以像素为单位)

    progress

    • 类型:布尔值
    • 默认值:true
    • 详情:是否在图标周围显示进度条

    样式

    你可以通过 CSS 变量来自定义 返回顶部 按钮的样式:

    :root {
    +  --back-to-top-z-index: 5;
    +  --back-to-top-icon: url("back-to-top.svg");
    +  --back-to-top-bg-color: #fff;
    +  --back-to-top-color: #3eaf7c;
    +  --back-to-top-color-hover: #71cda3;
    +  --back-to-top-shadow: rgb(0 0 0 / 20%);
    +}
    +
    + + + diff --git a/zh/plugins/container.html b/zh/plugins/container.html new file mode 100644 index 0000000000..8aac2bd46f --- /dev/null +++ b/zh/plugins/container.html @@ -0,0 +1,75 @@ + + + + + + + + + container | VuePress 生态系统 + + + + + +

    container

    @vuepress/plugin-container

    为你的 VuePress 站点注册自定义容器。

    该插件简化了 markdown-it-container在新窗口打开 的使用方法,但同时也保留了其原本的能力。

    默认主题的 自定义容器 就是由该插件支持的。

    使用方法

    npm i -D @vuepress/plugin-container@next
    +
    import { containerPlugin } from '@vuepress/plugin-container'
    +
    +export default {
    +  plugins: [
    +    containerPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    容器语法

    ::: <type> [info]
    +[content]
    +:::
    +
    • type 是必需的,应通过 type 配置项来指定。
    • info 是可选的,其默认值可以通过 localesdefaultInfo 配置项来指定。
    • content 可是任何合法的 Markdown 内容。

    提示

    该插件可以被多次使用,以便支持不同类型的容器。

    配置项

    type

    locales

    • 类型: Record<string, { defaultInfo: string }>

    • 详情:

      容器在不同 locales 下的默认 info

      如果没有指定该配置项,默认 info 会使用大写的 type

    • 示例:

    export default {
    +  plugins: [
    +    containerPlugin({
    +      type: 'tip',
    +      locales: {
    +        '/': {
    +          defaultInfo: 'TIP',
    +        },
    +        '/zh/': {
    +          defaultInfo: '提示',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    before

    • 类型: (info: string) => string

    • 默认值:

    (info: string): string =>
    +  `<div class="custom-container ${type}">${info ? `<p class="custom-container-title">${info}</p>` : ''}\n`
    +
    • 详情:

      一个用于渲染容器起始标签的函数。

      第一个参数是 容器语法info 部分。

      如果你没有设置 after 配置项,则该配置项也不会生效。

    after

    • 类型: (info: string) => string

    • 默认值:

    (): string => '</div>\n'
    +
    • 详情:

      一个用于渲染容器结束标签的函数。

      第一个参数是 容器语法info 部分。

      如果你没有设置 before 配置项,则该配置项也不会生效。

    render

    • 类型:
    type MarkdownItContainerRenderFunction = (
    +  tokens: Token[],
    +  index: number,
    +  options: any,
    +  env: MarkdownEnv,
    +  self: Renderer,
    +) => string
    +

    validate

    marker

    + + + diff --git a/zh/plugins/copy-code.html b/zh/plugins/copy-code.html new file mode 100644 index 0000000000..915d86b020 --- /dev/null +++ b/zh/plugins/copy-code.html @@ -0,0 +1,97 @@ + + + + + + + + + copy-code | VuePress 生态系统 + + + + + +

    copy-code

    @vuepress/plugin-copy-code

    此插件会自动在 PC 设备上为每个代码块右上角添加复制按钮。

    默认选择器匹配 @vuepress/theme-default,所以在你自己的主题中集成时可能需要调整它。

    使用

    npm i -D @vuepress/plugin-copy-code@next
    +
    import { copyCodePlugin } from '@vuepress/plugin-copy-code'
    +
    +export default {
    +  plugins: [
    +    copyCodePlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    选项

    selector

    • 类型:string | string[]

    • 默认值:'.theme-default-content div[class*="language-"] pre'

    • 详情:

      代码块选择器

    showInMobile

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否展示在移动端

    duration

    • 类型:number

    • 默认值:2000

    • 详情:

      提示消息显示时间,设置为 0 会禁用提示。

    delay

    • 类型:number

    • 默认值:800

    • 详情:

      注册复制按钮的延时,单位 ms。

      如果你使用的主题有切换动画,建议配置此选项为 切换动画时长 + 200

    locales

    • 类型:CopyCodeLocaleConfig

      interface CopyCodeLocaleData {
      +  /**
      +   * 复制文字
      +   */
      +  copy: string
      +
      +  /**
      +   * 已复制文字
      +   */
      +  copied: string
      +}
      +
      +interface CopyCodeLocaleConfig {
      +  [localePath: string]: CopyCodeLocaleData
      +}
      +
    • 必填:否

    • 详情:

      复制按钮插件的国际化配置。

    • 示例:

      import { copyCodePlugin } from 'vuepress-plugin-copy-code2'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // 这是一个支持的语言
      +      lang: 'zh-CN',
      +    },
      +    '/xx/': {
      +      // 这是一个没有收到插件支持的语言
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyCodePlugin({
      +      locales: {
      +        '/': {
      +          // 覆盖复制按钮标签文字
      +          copy: '复制此段代码',
      +        },
      +
      +        '/xx/': {
      +          // 在这里完整设置 `mm-NN` 的多语言配置
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    样式

    你可以通过 CSS 变量来自定义复制按钮的样式:

    :root {
    +  --code-copy-icon: url("copy-button.svg");
    +  --code-copied-icon: url("copied-button.svg");
    +  --copy-code-color: var(--code-ln-color, #9e9e9e);
    +  --copy-code-hover: var(--code-hl-bg-color, rgb(0 0 0 / 66%));
    +}
    +
    + + + diff --git a/zh/plugins/copyright.html b/zh/plugins/copyright.html new file mode 100644 index 0000000000..3503a35231 --- /dev/null +++ b/zh/plugins/copyright.html @@ -0,0 +1,102 @@ + + + + + + + + + copyright | VuePress 生态系统 + + + + + +

    copyright

    @vuepress/plugin-copyright

    此插件可以在访问者从你的站点复制内容时,自动追加版权信息,也可以禁止站点的复制或者选择。

    使用

    npm i -D @vuepress/plugin-copyright@next
    +
    import { copyrightPlugin } from '@vuepress/plugin-copyright'
    +
    +export default {
    +  plugins: [
    +    copyrightPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    启用版权信息

    此插件默认全局禁用。你可以:

    • 在特定页面的 frontmatter 中设置 copy: true 手动开启。
    • 在插件选项中设置 global: true 让其全局生效,并在页面的 frontmatter 中设置 copy: false 禁用它。

    处于不打扰用户的考虑,默认配置下仅当复制长度超过 100 时才会追加版权信息。如果你希望改变这个触发值,你可以插件选项中设置 triggerLength,或在页面 frontmatter 单独设置 copy.triggerLength

    你可以通过插件的 authorlicense 选项设置全局作者和协议信息。

    如果文档的不同部分拥有不同的作者和协议,你可以通过 authorGetterlicenseGetter 传入一个使用当前页面对象作为参数的函数 (page: Page) => string 并通过它返回相应信息。

    插件会默认通过模板从作者、协议和页面链接生成版权信息,并在复制时追加。如果你认为这不够灵活,你可以设置 copyrightGetter 返回一个完全由你自定义的版权信息,或返回 null 以使用默认模板。

    禁用复制和选择

    如果你希望禁止用户复制较长内容,你可以在插件选项中设置 maxLength 控制这个临界值,或在页面 frontmatter 单独设置 copy.maxLength

    • 如果你不希望用户复制你的整个站点或特定页面文字,你可以在插件选项中设置 disableCopy 或在页面 frontmatter 中设置 copy.disableCopy 来禁用复制,后者具有更高优先级。
    • 如果你不希望用户选择你的整个站点或特定页面文字,你可以在插件选项中设置 disableSelection 或在页面 frontmatter 中设置 copy.disableSelection 来禁用文字选择。此选项具有更高优先级

    插件选项

    author

    • 类型:string
    • 详情:默认作者信息

    license

    • 类型:string
    • 详情:默认协议信息

    authorGetter

    • 类型:(page: Page) => string | null
    • 详情:作者信息获取器

    licenseGetter

    • 类型:(page: Page) => string | null
    • 详情:协议信息获取器

    copyrightGetter

    • 类型:(page: Page) => string | null
    • 详情:协议信息获取器

    triggerLength

    • 类型:number
    • 默认值:100
    • 详情:触发附加版权的最小内容长度

    maxLength

    • 类型:number
    • 默认值:0
    • 详情:允许复制的最大内容长度,0 意味着无限制。

    global

    • 类型:boolean
    • 默认值:false
    • 详情:是否全局启用

    disableCopy

    • 类型:boolean
    • 默认值:false
    • 详情:禁用复制

    disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择

    canonical

    • 类型:string

    • 详情:

      首选部署位置。

    例子

    如果你在 https://myblog.comhttps://blog.com/username/ 下部署相同的内容,你可能希望选择一个站点作为首选链接。

    • 如果你倾向于使用第一个,你应该将 canonical 设置为 https://myblog.com
    • 如果你倾向于使用第二个,你应该将 canonical 设置为 https://blog.com/username/

    这样,在另一个站点触发的版权信息也会指向你的首选站点。

    locales

    • 类型:CopyrightLocaleConfig

      interface CopyrightLocaleData {
      +  /**
      +   * 作者文字
      +   *
      +   * @description `:author` 将会被作者替换
      +   */
      +  author: string
      +
      +  /**
      +   * 协议文字
      +   *
      +   * @description `:license` 会被当前协议替换
      +   */
      +  license: string
      +
      +  /**
      +   * 链接文字
      +   *
      +   * @description `:link` 会替换为当前页面链接
      +   */
      +  link: string
      +}
      +
      +interface CopyrightLocaleConfig {
      +  [localePath: string]: CopyrightLocaleData
      +}
      +
    • 详情:版权插件的国际化配置。

    • 示例:

      import { copyrightPlugin } from '@vuepress/plugin-copyright'
      +
      +export default {
      +  locales: {
      +    '/': {
      +      // this is a supported language
      +      lang: 'en-US',
      +    },
      +    '/xx/': {
      +      // the plugin does not support this language
      +      lang: 'mm-NN',
      +    },
      +  },
      +
      +  plugins: [
      +    copyrightPlugin({
      +      locales: {
      +        '/': {
      +          // Override link text
      +          link: 'Original posted at :link',
      +        },
      +
      +        '/xx/': {
      +          // Complete locale config for `mm-NN` language here
      +        },
      +      },
      +    }),
      +  ],
      +}
      +
    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    Frontmatter

    copy.triggerLength

    • 类型:number
    • 默认值:100
    • 详情: 触发附加版权的最小内容长度

    copy.maxLength

    • 类型:number
    • 默认值:0
    • 详情: 允许复制的最大内容长度,0 意味着无限制。

    copy.disableCopy

    • 类型:boolean
    • 默认值:false
    • 详情:禁用复制

    copy.disableSelection

    • 类型:boolean
    • 默认值:false
    • 详情:禁用选择
    + + + diff --git a/zh/plugins/docsearch.html b/zh/plugins/docsearch.html new file mode 100644 index 0000000000..50403b73fe --- /dev/null +++ b/zh/plugins/docsearch.html @@ -0,0 +1,239 @@ + + + + + + + + + docsearch | VuePress 生态系统 + + + + + +

    docsearch

    @vuepress/plugin-docsearch

    Algolia DocSearch在新窗口打开 集成到 VuePress 中,为你的文档网站提供搜索功能。

    提示

    当你正确配置该插件后,默认主题会把 DocSearch 按钮添加到导航栏。

    该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。

    使用方法

    npm i -D @vuepress/plugin-docsearch@next
    +
    import { docsearchPlugin } from '@vuepress/plugin-docsearch'
    +
    +export default {
    +  plugins: [
    +    docsearchPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    获取搜索索引

    你需要 提交你的网站 URL在新窗口打开 来加入 DocSearch 项目。当你的索引成功创建后, DocSearch 团队会将 apiKeyindexName 发送到你的邮箱。接下来,你就可以配置该插件,在 VuePress 中启用 DocSearch 了。

    或者,你也可以 运行你自己的爬虫在新窗口打开 来创建索引,然后使用你自己的 appId, apiKeyindexName 来配置该插件。

    官方爬虫配置示例
    new Crawler({
    +  appId: 'YOUR_APP_ID',
    +  apiKey: 'YOUR_API_KEY',
    +  rateLimit: 8,
    +  startUrls: [
    +    // 这是 Algolia 开始抓取网站的初始地址
    +    // 如果你的网站被分为数个独立部分,你可能需要在此设置多个入口链接
    +    'https://YOUR_WEBSITE_URL/',
    +  ],
    +  sitemaps: [
    +    // 如果你在使用 Sitemap 插件 (如: @vuepress-plugin/sitemap),你可以提供 Sitemap 链接
    +    'https://YOUR_WEBSITE_URL/sitemap.xml',
    +  ],
    +  ignoreCanonicalTo: false,
    +  exclusionPatterns: [
    +    // 你可以通过它阻止 Algolia 抓取某些 URL
    +  ],
    +  discoveryPatterns: [
    +    // 这是 Algolia 抓取 URL 的范围
    +    'https://YOUR_WEBSITE_URL/**',
    +  ],
    +  // 爬虫执行的计划时间,可根据文档更新频率设置
    +  schedule: 'at 02:00 every 1 day',
    +  actions: [
    +    // 你可以拥有多个 action,特别是你在一个域名下部署多个文档时
    +    {
    +      // 使用适当的名称为索引命名
    +      indexName: 'YOUR_INDEX_NAME',
    +      // 索引生效的路径
    +      pathsToMatch: ['https://YOUR_WEBSITE_URL/**'],
    +      // 控制 Algolia 如何抓取你的站点
    +      recordExtractor: ({ $, helpers }) => {
    +        // @vuepress/theme-default 的选项
    +        return helpers.docsearch({
    +          recordProps: {
    +            lvl0: {
    +              selectors: '.sidebar-heading.active',
    +              defaultValue: 'Documentation',
    +            },
    +            lvl1: '.theme-default-content h1',
    +            lvl2: '.theme-default-content h2',
    +            lvl3: '.theme-default-content h3',
    +            lvl4: '.theme-default-content h4',
    +            lvl5: '.theme-default-content h5',
    +            lvl6: '.theme-default-content h6',
    +            content: '.theme-default-content p, .theme-default-content li',
    +          },
    +          indexHeadings: true,
    +        })
    +      },
    +    },
    +  ],
    +  initialIndexSettings: {
    +    // 控制索引如何被初始化,这仅当索引尚未生成时有效
    +    // 你可能需要在修改后手动删除并重新生成新的索引
    +    YOUR_INDEX_NAME: {
    +      attributesForFaceting: ['type', 'lang'],
    +      attributesToRetrieve: ['hierarchy', 'content', 'anchor', 'url'],
    +      attributesToHighlight: ['hierarchy', 'hierarchy_camel', 'content'],
    +      attributesToSnippet: ['content:10'],
    +      camelCaseAttributes: ['hierarchy', 'hierarchy_radio', 'content'],
    +      searchableAttributes: [
    +        'unordered(hierarchy_radio_camel.lvl0)',
    +        'unordered(hierarchy_radio.lvl0)',
    +        'unordered(hierarchy_radio_camel.lvl1)',
    +        'unordered(hierarchy_radio.lvl1)',
    +        'unordered(hierarchy_radio_camel.lvl2)',
    +        'unordered(hierarchy_radio.lvl2)',
    +        'unordered(hierarchy_radio_camel.lvl3)',
    +        'unordered(hierarchy_radio.lvl3)',
    +        'unordered(hierarchy_radio_camel.lvl4)',
    +        'unordered(hierarchy_radio.lvl4)',
    +        'unordered(hierarchy_radio_camel.lvl5)',
    +        'unordered(hierarchy_radio.lvl5)',
    +        'unordered(hierarchy_radio_camel.lvl6)',
    +        'unordered(hierarchy_radio.lvl6)',
    +        'unordered(hierarchy_camel.lvl0)',
    +        'unordered(hierarchy.lvl0)',
    +        'unordered(hierarchy_camel.lvl1)',
    +        'unordered(hierarchy.lvl1)',
    +        'unordered(hierarchy_camel.lvl2)',
    +        'unordered(hierarchy.lvl2)',
    +        'unordered(hierarchy_camel.lvl3)',
    +        'unordered(hierarchy.lvl3)',
    +        'unordered(hierarchy_camel.lvl4)',
    +        'unordered(hierarchy.lvl4)',
    +        'unordered(hierarchy_camel.lvl5)',
    +        'unordered(hierarchy.lvl5)',
    +        'unordered(hierarchy_camel.lvl6)',
    +        'unordered(hierarchy.lvl6)',
    +        'content',
    +      ],
    +      distinct: true,
    +      attributeForDistinct: 'url',
    +      customRanking: [
    +        'desc(weight.pageRank)',
    +        'desc(weight.level)',
    +        'asc(weight.position)',
    +      ],
    +      ranking: [
    +        'words',
    +        'filters',
    +        'typo',
    +        'attribute',
    +        'proximity',
    +        'exact',
    +        'custom',
    +      ],
    +      highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
    +      highlightPostTag: '</span>',
    +      minWordSizefor1Typo: 3,
    +      minWordSizefor2Typos: 7,
    +      allowTyposOnNumericTokens: false,
    +      minProximity: 1,
    +      ignorePlurals: true,
    +      advancedSyntax: true,
    +      attributeCriteriaComputedByMinProximity: true,
    +      removeWordsIfNoResults: 'allOptional',
    +    },
    +  },
    +})
    +

































     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     







     
































































    上述 recordProps 是用于默认主题的配置,你可以根据你使用的主题来修改它们。

    注意 initialIndexSettings.YOUR_INDEX_NAME.attributesForFaceting 字段必须包含 'lang',否则该插件将无法正常工作。

    提示

    如果你使用的不是默认主题,或者在使用 Docsearch 的时候遇到了任何问题,你也可以检查上述的爬虫配置示例,然后前往 Algolia Crawler在新窗口打开 仓库,在你项目侧边栏中的 Editor 页面中修改你的配置。

    配置项

    apiKey

    indexName

    appId

    searchParameters

    placeholder

    disableUserPersonalization

    initialQuery

    translations

    locales

    • 类型: Record<string, DocsearchPluginOptions>

    • 详情:

      在不同 locales 下对该插件进行不同的配置。

      该插件的所有其他选项都可以在 locale 中进行配置。

    • 示例:

    export default {
    +  plugins: [
    +    docsearchPlugin({
    +      appId: '<APP_ID>',
    +      apiKey: '<API_KEY>',
    +      indexName: '<INDEX_NAME>',
    +      locales: {
    +        '/': {
    +          placeholder: 'Search Documentation',
    +          translations: {
    +            button: {
    +              buttonText: 'Search Documentation',
    +            },
    +          },
    +        },
    +        '/zh/': {
    +          placeholder: '搜索文档',
    +          translations: {
    +            button: {
    +              buttonText: '搜索文档',
    +            },
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    indexBase

    • 类型: string

    • 默认值: base

    • 详情:

      搜索索引基础路径。

      如果你需要把你的站点部署到不同的域名上,你不需要把它们全都提交到 Docsearch 上来分别生成搜索索引。你可以选择其中一个域名作为 索引域名 ,并且仅将 索引域名 提交到 DocSearch 上来爬去搜索索引。然后,你就可以在不同的部署域名下复用索引。

      如果你不同部署域名下的 base 是不一样的,你就需要将这个配置设置成 索引域名base ,这样其他的部署域名就可以正确复用索引了。

    injectStyles

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否注入 DocSearch 的默认样式。

      如果你认为 DocSearch 的默认样式和你的站点不兼容,你可以尝试覆盖默认样式,或者将该选项设置为 false 来完全移除默认样式。

      当该选项被禁用时,你需要为 DocSearch 引入你自己的样式。同时要注意,你也无法再使用 样式 章节中提到的样式自定义能力。

    样式

    你可以通过 @docsearch/css在新窗口打开 提供的 CSS 变量来自定义样式:

    :root {
    +  --docsearch-primary-color: rgb(84, 104, 255);
    +  --docsearch-text-color: rgb(28, 30, 33);
    +  --docsearch-spacing: 12px;
    +  --docsearch-icon-stroke-width: 1.4;
    +  --docsearch-highlight-color: var(--docsearch-primary-color);
    +  --docsearch-muted-color: rgb(150, 159, 175);
    +  --docsearch-container-background: rgba(101, 108, 133, 0.8);
    +  --docsearch-logo-color: rgba(84, 104, 255);
    +
    +  /* modal */
    +  --docsearch-modal-width: 560px;
    +  --docsearch-modal-height: 600px;
    +  --docsearch-modal-background: rgb(245, 246, 247);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 0 3px
    +      8px 0 rgba(85, 90, 100, 1);
    +
    +  /* searchbox */
    +  --docsearch-searchbox-height: 56px;
    +  --docsearch-searchbox-background: rgb(235, 237, 240);
    +  --docsearch-searchbox-focus-background: #fff;
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);
    +
    +  /* hit */
    +  --docsearch-hit-height: 56px;
    +  --docsearch-hit-color: rgb(68, 73, 80);
    +  --docsearch-hit-active-color: #fff;
    +  --docsearch-hit-background: #fff;
    +  --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225);
    +
    +  /* key */
    +  --docsearch-key-gradient: linear-gradient(
    +    -225deg,
    +    rgb(213, 219, 228) 0%,
    +    rgb(248, 248, 248) 100%
    +  );
    +  --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), inset 0 0 1px 1px
    +      #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4);
    +
    +  /* footer */
    +  --docsearch-footer-height: 44px;
    +  --docsearch-footer-background: #fff;
    +  --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
    +}
    +

    组件

    Docsearch

    • 详情:

      该插件会全局注册一个 <Docsearch /> 组件,你可以不传入任何 Props 来使用它。

      将该组件放置在你想要显示 docsearch 按钮的地方。例如,默认主题将这个组件放在了导航栏的末尾。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    + + + diff --git a/zh/plugins/external-link-icon.html b/zh/plugins/external-link-icon.html new file mode 100644 index 0000000000..802a54b2ec --- /dev/null +++ b/zh/plugins/external-link-icon.html @@ -0,0 +1,64 @@ + + + + + + + + + external-link-icon | VuePress 生态系统 + + + + + +

    external-link-icon

    @vuepress/plugin-external-link-icon

    该插件会为你 Markdown 内容中的外部链接添加一个图标,即 在新窗口打开

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-external-link-icon@next
    +
    import { externalLinkIconPlugin } from '@vuepress/plugin-external-link-icon'
    +
    +export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    locales

    • 类型: Record<string, { openInNewWindow: string }>

    • 详情:

      外部链接图标在不同 locales 下的 A11y 文字。

      如果没有指定该配置项,它会降级使用默认文字。

    • 示例:

    export default {
    +  plugins: [
    +    externalLinkIconPlugin({
    +      locales: {
    +        '/': {
    +          openInNewWindow: 'open in new window',
    +        },
    +        '/zh/': {
    +          openInNewWindow: '在新窗口打开',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    Frontmatter

    externalLinkIcon

    • 类型: boolean

    • 详情:

      是否在当前页面的外部链接的后面添加外部链接图标。

    样式

    你可以通过 CSS 变量来自定义外部链接图标的样式:

    :root {
    +  --external-link-icon-color: #aaa;
    +}
    +

    组件

    ExternalLinkIcon

    • 详情:

      该插件会全局注册一个 <ExternalLinkIcon /> 组件,你可以不传入任何 Props 来使用它。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    + + + diff --git a/zh/plugins/feed/channel.html b/zh/plugins/feed/channel.html new file mode 100644 index 0000000000..a0504af002 --- /dev/null +++ b/zh/plugins/feed/channel.html @@ -0,0 +1,51 @@ + + + + + + + + + 频道设置 | VuePress 生态系统 + + + + + +

    频道设置

    channel 插件选项用于配置 feed 的频道。

    channel.title

    • 类型:string
    • 默认值:SiteConfig.title

    频道的标题

    • 类型:string
    • 默认值:部署的网址 (通过 options.hostnamecontext.base 生成)

    频道地址

    channel.description

    • 类型:string
    • 默认值:SiteConfig.description

    频道描述信息

    channel.language

    • 类型:string
    • 默认值:
      • siteConfig.locales['/'].locales
      • 如果上述未提供,回退到 "en-US"

    频道使用的语言

    • 类型:string
    • 默认值:
      • 尝试读取 channel 选项中的 author.name 生成 Copyright by $author
    • 建议自行设置:

    频道版权信息

    channel.pubDate

    • 类型:string (需是合法的 Date ISOString)
    • 默认值:每次插件构建时刻
    • 建议自行设置:

    频道内容的发布时间

    channel.lastUpdated

    • 类型:string (需是合法的 Date ISOString)
    • 默认值:每次插件构建时刻

    频道内容的上次更新时间

    channel.ttl

    • 类型:number
    • 建议自行设置:

    内容有效时间,即获取后保持缓存而不进行新获取的时间

    channel.image

    • 类型:string
    • 建议自行设置:

    这是一个会在频道中使用的图片,建议设置正方形图片、尺寸最好不小于 512×512。

    channel.icon

    • 类型:string
    • 建议自行设置:

    一个代表频道的图标,建议设置正方形图片、尺寸最好不小于 128×128,背景色透明。

    channel.author

    • 类型:FeedAuthor
    • 建议自行设置:

    频道的作者。

    FeedAuthor 格式
    interface FeedAuthor {
    +  /** 作者姓名 */
    +  name: string
    +  /** 作者电子邮箱 */
    +  email?: string
    +  /** 作者网站 */
    +  url?: string
    +  /**
    +   * 作者头像地址
    +   *
    +   * 正方形,最好不小于 128×128,透明背景
    +   */
    +  avatar?: string
    +}
    +

    channel.hub

    • 类型:string

    Websub 的链接。Websub 需要服务器后端,与 VuePress 主旨不符,如无特殊需要忽略即可。

    WebSub

    有关信息,详见 Websub在新窗口打开

    + + + diff --git a/zh/plugins/feed/config.html b/zh/plugins/feed/config.html new file mode 100644 index 0000000000..141bc0fe81 --- /dev/null +++ b/zh/plugins/feed/config.html @@ -0,0 +1,54 @@ + + + + + + + + + 插件配置 | VuePress 生态系统 + + + + + +

    插件配置

    hostname

    • 类型:string
    • 必填:是

    部署网站的域名。

    atom

    • 类型:boolean
    • 默认值:false

    是否启用 Atom 格式输出。

    json

    • 类型:boolean
    • 默认值:false

    是否启用 JSON 格式输出。

    rss

    • 类型:boolean
    • 默认值:false

    是否启用 RSS 格式输出。

    image

    • 类型:string

    一个大的图片,用作 feed 展示。

    icon

    • 类型:string

    一个小的图标,显示在订阅列表中。

    count

    • 类型:number
    • 默认值:100

    设置 feed 的最大项目数量。在所有页面排序好后,插件会截取前 count 个项目。

    如果你的站点文章很多,你应该考虑设置这个选项以减少 feed 文件大小。

    preservedElements

    • 类型:(RegExp | string)[] | (tagName:string) => boolean

    应在 Feed 中保留的自定义元素或组件。

    默认情况下,所有未知标签均会被移除。

    filter

    • 类型:(page: Page)=> boolean

    • 默认值:

      ;({ frontmatter, filePathRelative }: Page): boolean =>
      +  !(
      +    frontmatter.home ||
      +    !filePathRelative ||
      +    frontmatter.article === false ||
      +    frontmatter.feed === false
      +  )
      +

    自定义的过滤函数,用于过滤哪些项目在 feed 中显示。

    sorter

    • 类型: (pageA: Page, pageB: Page)=> number

    • 默认值:

      // dateSorter 来源于 @vuepress/helper
      +;(pageA, pageB): number =>
      +  dateSorter(
      +    pageA.data.git?.createdTime
      +      ? new Date(pageA.data.git?.createdTime)
      +      : pageA.frontmatter.date,
      +    pageB.data.git?.createdTime
      +      ? new Date(pageB.data.git?.createdTime)
      +      : pageB.frontmatter.date,
      +  )
      +

    Feed 项目的排序器。

    默认的排序行为是通过 Git 的文件添加日期 (需要 @vuepress/plugin-git)。

    提示

    你应该启用 @vuepress/plugin-git 来获取最新创建的页面作为 feed 项目。否则,feed 项目将按照 VuePress 中页面的默认顺序排序。

    channel

    channel 选项用于配置 Feed 频道。

    可用选项详见 配置 → 频道设置

    devServer

    • 类型:boolean
    • 默认值:false

    是否在开发服务器中启用

    提示

    由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。

    devHostname

    • 类型:string
    • 默认值:"http://localhost:${port}"

    开发服务器使用的主机名

    atomOutputFilename

    • 类型:string
    • 默认值:"atom.xml"

    Atom 格式输出路径,相对于输出路径。

    atomXslTemplate

    • 类型:string
    • 默认值:@vuepress/plugin-feed/templates/atom.xsl 的内容

    Atom xsl 模板文件没人陪美国

    atomXslFilename

    • 类型:string
    • 默认值:"atom.xsl"

    Atom xsl 输出路径,相对于输出路径。

    jsonOutputFilename

    • 类型:string
    • 默认值:"feed.json"

    JSON 格式输出路径,相对于输出路径。

    rssOutputFilename

    • 类型:string
    • 默认值:"rss.xml"

    RSS 格式输出路径,相对于输出路径。

    rssXslTemplate

    • 类型:string
    • 默认值:@vuepress/plugin-feed/templates/rss.xsl 的内容

    RSS xsl 模板文件内容。

    rssXslFilename

    • 类型:string
    • 默认值:"rss.xsl"

    RSS xsl 输出路径,相对于输出路径。

    getter

    Feed 生成控制器,详见 Feed 生成器

    此插件内置了生成器,只有当你想完全控制 feed 生成时才需要设置此选项。

    locales

    • 类型:Record<string, BaseFeedOptions>

    你可以将它用于每个语言环境的特定选项。

    hostname 外,上述任何选项均受支持。

    + + + diff --git a/zh/plugins/feed/frontmatter.html b/zh/plugins/feed/frontmatter.html new file mode 100644 index 0000000000..73ae6ce0ef --- /dev/null +++ b/zh/plugins/feed/frontmatter.html @@ -0,0 +1,87 @@ + + + + + + + + + Frontmatter 配置 | VuePress 生态系统 + + + + + +

    Frontmatter 配置

    你可以通过配置每个页面的 Frontmatter,来对每个 Feed 项目生成进行单独的控制。

    添加与移除

    默认情况下,所有文章均会被添加至 feed 流。如果你想在 feed 中移除特定页面,你可以在 frontmatter 中设置 feed: false

    读取的 Frontmatter 信息

    title

    • 类型:string

    由 VuePress 自动生成,默认为页面的 h1 内容

    description

    • 类型:string

    页面描述

    date

    • 类型:Date

    页面的发布日期

    article

    • 类型:boolean

    该页面是否是文章

    如果此项设置为 false,则该页不会包含在最终的 feed 中。

    • 类型:string

    页面版权信息

    cover / image / banner

    • 类型:string

    页面的封面/分享图,需为完整链接或绝对链接。

    Frontmatter 选项

    feed.title

    • 类型:string

    Feed 项目的标题

    feed.description

    • 类型:string

    Feed 项目的描述

    feed.content

    • 类型:string

    Feed 项目的内容

    feed.author

    • 类型:FeedAuthor[] | FeedAuthor

    Feed 项目的作者

    FeedAuthor 格式
    interface FeedAuthor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.contributor

    • 类型:FeedContributor[] | FeedContributor

    Feed 项目的贡献者

    FeedContributor 格式
    interface FeedContributor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    feed.guid

    • 类型:string

    Feed 项目的标识符,用于标识 Feed 项目。

    你应该确保每个 Feed 项目有全局唯一的 guid。

    + + + diff --git a/zh/plugins/feed/getter.html b/zh/plugins/feed/getter.html new file mode 100644 index 0000000000..aa38331026 --- /dev/null +++ b/zh/plugins/feed/getter.html @@ -0,0 +1,127 @@ + + + + + + + + + Feed 获取器 | VuePress 生态系统 + + + + + +

    Feed 获取器

    你可以通过控制插件选项中的 getter 来完全控制 Feed 项目的生成。

    getter.title

    • 类型:(page: Page) => string

    项目标题获取器

    • 类型:(page: Page) => string

    项目链接获取器

    getter.description

    • 类型:(page: Page) => string | undefined

    项目描述获取器

    提示

    因为 Atom 在摘要中支持 HTML,所以如果可能的话,你可以在这里返回 HTML 内容,但内容必须以标记 html: 开头。

    getter.content

    • 类型:(page: Page) => string

    项目内容获取器

    getter.author

    • 类型:(page: Page) => FeedAuthor[]

    项目作者获取器。

    获取器应在作者信息缺失时返回空数组。

    FeedAuthor 格式
    interface FeedAuthor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +

    getter.category

    • 类型:(page: Page) => FeedCategory[] | undefined

    项目分类获取器。

    FeedCategory 格式
    interface FeedCategory {
    +  /**
    +   * 分类名称
    +   */
    +  name: string
    +
    +  /**
    +   * 标识分类法的字符串
    +   *
    +   * @description rss format only
    +   */
    +  domain?: string
    +
    +  /**
    +   * URI 标识的分类 scheme
    +   *
    +   * @description atom format only
    +   */
    +  scheme?: string
    +}
    +

    getter.enclosure

    • 类型:(page: Page) => FeedEnclosure | undefined

    项目附件获取器。

    FeedEnclosure 格式
    interface FeedEnclosure {
    +  /**
    +   * Enclosure 地址
    +   */
    +  url: string
    +
    +  /**
    +   * 类型
    +   *
    +   * @description 应为一个标准的 MIME 类型,rss format only
    +   */
    +  type: string
    +
    +  /**
    +   * 按照字节数计算的大小
    +   *
    +   * @description rss format only
    +   */
    +  length?: number
    +}
    +

    getter.publishDate

    • 类型:(page: Page) => Date | undefined

    项目发布日期获取器

    getter.lastUpdateDate

    • 类型:(page: Page) => Date

    项目最后更新日期获取器

    getter.image

    • 类型:(page: Page) => string

    项目图片获取器

    确保返回一个完整的 URL。

    getter.contributor

    • 类型:(page: Page) => FeedContributor[]

    项目贡献者获取器

    获取器应在贡献者信息缺失时返回空数组。

    FeedContributor 格式
    interface FeedContributor {
    +  /**
    +   * 作者名字
    +   */
    +  name?: string
    +
    +  /**
    +   * 作者邮件
    +   */
    +  email?: string
    +
    +  /**
    +   * 作者网站
    +   *
    +   * @description json format only
    +   */
    +  url?: string
    +
    +  /**
    +   * 作者头像
    +   *
    +   * @description json format only
    +   */
    +  avatar?: string
    +}
    +
    • 类型:(page: Page) => string | undefined

    项目版权获取器

    + + + diff --git a/zh/plugins/feed/guide.html b/zh/plugins/feed/guide.html new file mode 100644 index 0000000000..38a6cc41e5 --- /dev/null +++ b/zh/plugins/feed/guide.html @@ -0,0 +1,37 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    使用

    插件可为你生成以下三种格式的 feed 文件:

    • Atom 1.0
    • JSON 1.1
    • RSS 2.0

    请按照需要生成的格式,在插件选项中设置 atom, jsonrsstrue

    为了正确生成 Feed 链接,你需要在插件选项中设置 hostname

    可读的预览

    当你在浏览器中打开 Feed 文件时,我们会通过 xsl 模板将 atom 和 rss feed xml 魔法般地转换为可读的 html。你可以查看本站的 atom在新窗口打开rss在新窗口打开 feed 作为案例!

    如果你想在开发服务器中预览 Feed,你需要在插件选项中设置 devServer: true。如果你没有使用默认的 http://localhost:{port},你还需要设置 devHostname

    频道设置

    你可以通过设置 channel 选项来自自定义 Feed 频道的各项信息。

    我们推荐进行如下设置:

    • 将建立 Feed 的日期转换为 ISOString 写入到 channel.pubDate
    • 通过 channel.ttl 中设置内容的更新周期(单位: 分钟)
    • 通过 channel.copyright 设置版权信息
    • 通过 channel.author 设置频道作者。

    详细的选项及其默认值详见 配置 → 频道设置

    Feed 生成

    默认情况下,所有文章均会被添加至 feed 流。

    你可以在 frontmatter 中配置 feed 和其他选项控制每个页面的 Feed 项目内容,详见 Frontmatter 选项 了解它们如何被转换。

    你可以通过配置插件选项中的 getter 完全控制 Feed 项目的生成逻辑。 详细的选项及其默认值详见 配置 → Feed 获取器

    多语言配置

    插件会针对每个语言生成单独的 Feed。

    你可以通过插件选项中的 locales 分别对不同语言提供不同的默认设置。

    + + + diff --git a/zh/plugins/feed/index.html b/zh/plugins/feed/index.html new file mode 100644 index 0000000000..74f2a797ee --- /dev/null +++ b/zh/plugins/feed/index.html @@ -0,0 +1,47 @@ + + + + + + + + + feed | VuePress 生态系统 + + + + + +

    feed

    @vuepress/plugin-feed

    使用

    npm i -D @vuepress/plugin-feed@next
    +
    import { feedPlugin } from '@vuepress/plugin-feed'
    +
    +export default {
    +  plugins: [
    +    feedPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/git.html b/zh/plugins/git.html new file mode 100644 index 0000000000..9298787901 --- /dev/null +++ b/zh/plugins/git.html @@ -0,0 +1,66 @@ + + + + + + + + + git | VuePress 生态系统 + + + + + +

    git

    @vuepress/plugin-git

    该插件会收集你的页面的 Git 信息,包括创建和更新时间、贡献者等。

    默认主题的 lastUpdatedcontributors 就是由该插件支持的。

    该插件主要用于开发主题,大部分情况下你不需要直接使用它。

    使用方法

    npm i -D @vuepress/plugin-git@next
    +
    import { gitPlugin } from '@vuepress/plugin-git'
    +
    +export default {
    +  plugins: [
    +    gitPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    Git 仓库

    该插件要求你的项目在 Git 仓库在新窗口打开 下,这样它才能从提交历史记录中收集信息。

    在构建站点时,你应该确保所有的提交记录是可以获取到的。举例来说, CI 工作流通常会在克隆你的仓库时添加 --depth 1在新窗口打开 参数来避免拉取全部的提交记录,因此你需要禁用这个功能,以便该插件在 CI 可以中正常使用。

    注意

    该插件会显著降低准备数据的速度,特别是在你的页面数量很多的时候。你可以考虑在 dev 模式下禁用该插件来获取更好的开发体验。

    配置项

    createdTime

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的创建时间。

    updatedTime

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的更新时间。

    contributors

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否收集页面的贡献者。

    Frontmatter

    gitInclude

    • 类型: string[]

    • 详情:

      文件相对路径组成的数组,该数组中的文件会在计算页面数据时被包含在内。

    • 示例:

    ---
    +gitInclude:
    +  - relative/path/to/file1
    +  - relative/path/to/file2
    +---
    +

    页面数据

    该插件会向页面数据中添加一个 git 字段。

    在使用该插件后,可以在页面数据中获取该插件收集到的 Git 信息:

    import type { GitPluginPageData } from '@vuepress/plugin-git'
    +import { usePageData } from 'vuepress/client'
    +
    +export default {
    +  setup() {
    +    const page = usePageData<GitPluginPageData>()
    +    console.log(page.value.git)
    +  },
    +}
    +

    git.createdTime

    • 类型: number

    • 详情:

      页面第一次提交的 Unix 毫秒时间戳。

      该属性将取当前页面及 gitInclude 中所列文件的第一次提交的时间戳的最小值。

    git.updatedTime

    • 类型: number

    • 详情:

      页面最后一次提交的 Unix 毫秒时间戳。

      该属性将取当前页面及 gitInclude 中所列文件的最后一次提交的时间戳的最大值。

    git.contributors

    • 类型: GitContributor[]
    interface GitContributor {
    +  name: string
    +  email: string
    +  commits: number
    +}
    +
    • 详情:

      页面的贡献者信息。

      该属性将会包含 gitInclude 所列文件的贡献者。

    + + + diff --git a/zh/plugins/google-analytics.html b/zh/plugins/google-analytics.html new file mode 100644 index 0000000000..50b2999529 --- /dev/null +++ b/zh/plugins/google-analytics.html @@ -0,0 +1,62 @@ + + + + + + + + + google-analytics | VuePress 生态系统 + + + + + +

    google-analytics

    @vuepress/plugin-google-analytics

    Google Analytics在新窗口打开 集成到 VuePress 中。

    该插件会通过引入 gtag.js在新窗口打开 来启用 Google Analytics 4在新窗口打开

    使用方法

    npm i -D @vuepress/plugin-google-analytics@next
    +
    import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
    +
    +export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    上报事件

    Google Analytics 会 自动收集部分事件在新窗口打开 ,比如 page_view, first_visit 等。

    因此,如果你只是想收集站点的一些基础数据,你只需要正确设置 Measurement ID ,不需要再额外做其他事情。

    在引入该插件之后,一个全局的 gtag() 函数会被挂载到 window 对象上,你可以使用它进行 自定义事件的上报在新窗口打开

    配置项

    id

    • 类型: string

    • 详情:

      Google Analytics 4 的 Measurement ID ,应以 'G-' 开头。

      你可以通过 这里在新窗口打开 的指引来找到你的 Measurement ID 。注意区分 Google Analytics 4 的 Measurement ID (即 "G-" 开头的 ID) 和 Universal Analytics 的 Tracking ID (即 "UA-" 开头的 ID)。

    • 示例:

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +    }),
    +  ],
    +}
    +

    debug

    export default {
    +  plugins: [
    +    googleAnalyticsPlugin({
    +      id: 'G-XXXXXXXXXX',
    +      debug: true,
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/medium-zoom.html b/zh/plugins/medium-zoom.html new file mode 100644 index 0000000000..ec4234c9c3 --- /dev/null +++ b/zh/plugins/medium-zoom.html @@ -0,0 +1,67 @@ + + + + + + + + + medium-zoom | VuePress 生态系统 + + + + + +

    medium-zoom

    @vuepress/plugin-medium-zoom

    medium-zoom在新窗口打开 集成到 VuePress 中,为图片提供可缩放的功能。

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-medium-zoom@next
    +
    import { mediumZoomPlugin } from '@vuepress/plugin-medium-zoom'
    +
    +export default {
    +  plugins: [
    +    mediumZoomPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    selector

    • 类型: string

    • 默认值: ':not(a) > img'

    • 详情:

      可缩放的图片的选择器。

      默认情况下,该插件会使 <a> 标签以外的所有图片都支持缩放。

    delay

    • 类型: number

    • 默认值: 500

    • 详情:

      以毫秒为单位的延迟。

      在切换路由进入一个新页面时,该插件会在一定延迟后才使页面内的图片支持缩放。

    zoomOptions

    样式

    你可以通过 zoomOptions 对大部分的缩放样式进行自定义,不过作为补充,该插件同样提供了一些 CSS 变量:

    :root {
    +  --medium-zoom-z-index: 100;
    +  --medium-zoom-bg-color: #ffffff;
    +  --medium-zoom-opacity: 1;
    +}
    +

    Composition API

    useMediumZoom

    • 详情:

      返回该插件使用的 Zoom 实例,便于你直接使用实例上的 methods在新窗口打开

      该插件会在切换路由进入当前页面时使图片支持缩放。但如果你要动态添加新图片,那么你可能就需要这个方法来让这些新图片也支持缩放。

      该插件在 Zoom 实例上额外添加了一个 refresh 方法,它将使用 selector 作为默认参数,先调用 zoom.detach() 再调用 zoom.attach() ,便于你快速刷新当前页面图片的缩放状态。

    • 示例:

    import { nextTick } from 'vue'
    +import { useMediumZoom } from '@vuepress/plugin-medium-zoom/client'
    +
    +export default {
    +  setup() {
    +    const zoom = useMediumZoom()
    +
    +    // ... 进行了一些操作,在当前页面添加了新的图片
    +
    +    // 此时你可能需要手动调用 `refresh` 来让这些新图片支持缩放
    +    nextTick(() => {
    +      zoom.refresh()
    +    })
    +  },
    +}
    +
    + + + diff --git a/zh/plugins/nprogress.html b/zh/plugins/nprogress.html new file mode 100644 index 0000000000..ba1019e1ed --- /dev/null +++ b/zh/plugins/nprogress.html @@ -0,0 +1,47 @@ + + + + + + + + + nprogress | VuePress 生态系统 + + + + + +

    nprogress 插件

    @vuepress/plugin-nprogress

    nprogress在新窗口打开 集成到 VuePress 中,在切换到另一个页面时会展示进度条。

    该插件已经集成到默认主题中。

    使用方法

    npm i -D @vuepress/plugin-nprogress@next
    +
    import { nprogressPlugin } from '@vuepress/plugin-nprogress'
    +
    +export default {
    +  plugins: [nprogressPlugin()],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义进度条的样式:

    :root {
    +  --nprogress-color: #29d;
    +  --nprogress-z-index: 1031;
    +}
    +
    + + + diff --git a/zh/plugins/palette.html b/zh/plugins/palette.html new file mode 100644 index 0000000000..e5a9a90f34 --- /dev/null +++ b/zh/plugins/palette.html @@ -0,0 +1,75 @@ + + + + + + + + + palette | VuePress 生态系统 + + + + + +

    palette

    @vuepress/plugin-palette

    为你的主题提供调色板功能。

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    对于主题作者,该插件可以帮助你提供用户自定义样式的能力。

    使用方法

    npm i -D @vuepress/plugin-palette@next
    +
    import { palettePlugin } from '@vuepress/plugin-palette'
    +
    +export default {
    +  plugins: [
    +    palettePlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    调色板和样式

    该插件会提供一个 @vuepress/plugin-palette/palette (调色板文件)和一个 @vuepress/plugin-palette/style (样式文件),用于在你的主题样式中引入。

    调色板文件用于定义样式变量,因此它一般会在你主题样式的开头引入。举例来说,用户可以在调色板中定义 CSS 变量在新窗口打开SASS 变量在新窗口打开LESS 变量在新窗口打开Stylus 变量在新窗口打开 ,然后你可以在你的主题样式中使用这些变量。

    样式文件用于覆盖默认样式或添加额外样式,因此它一般会在你主题样式的末尾引入。

    使用

    在你的主题中使用该插件,假设你使用 SASS 作为 CSS 预处理器:

    export default {
    +  // ...
    +  plugins: [palettePlugin({ preset: 'sass' })],
    +}
    +

    使用调色板

    在你主题需要使用对应变量的地方引入该插件的调色板文件,比如在 Layout.vue 中:

    <template>
    +  <h1 class="palette-title">你好,调色板!</h1>
    +</template>
    +
    +<style lang="scss">
    +/* 从该插件的调色板中引入变量 */
    +@import '@vuepress/plugin-palette/palette';
    +
    +/* 设置变量的默认值 */
    +$color: red !default;
    +
    +/* 在你的样式中使用变量 */
    +.palette-title {
    +  color: $color;
    +}
    +</style>
    +

    然后,用户就可以在 .vuepress/styles/palette.scss 中自定义变量:

    $color: green;
    +

    使用样式

    在你主题的样式之后引入该插件的样式文件,比如在 clientConfigFile 中:

    // 引入你主题本身的样式文件
    +import 'path/to/your/theme/style'
    +// 引入该插件的样式文件
    +import '@vuepress/plugin-palette/style'
    +

    然后,用户就可以在 .vuepress/styles/index.scss 中添加额外样式,并可以覆盖你主题本身的样式:

    h1 {
    +  font-size: 2.5rem;
    +}
    +

    配置项

    preset

    • 类型: 'css' | 'sass' | 'less' | 'stylus'

    • 默认值: 'css'

    • 详情:

      设置其他选项的预设。

      如果你没有对该插件进行进阶定制化的需要,建议只设置该配置项并忽略其他选项。

    userPaletteFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/palette.css'
      • sass: '.vuepress/styles/palette.scss'
      • less: '.vuepress/styles/palette.less'
      • stylus: '.vuepress/styles/palette.styl'
    • 详情:

      用户调色板文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户定义样式变量,建议保持默认值作为约定的文件路径。

    tempPaletteFile

    • 类型: string

    • 默认值:

      • css: 'styles/palette.css'
      • sass: 'styles/palette.scss'
      • less: 'styles/palette.less'
      • stylus: 'styles/palette.styl'
    • 详情:

      生成的调色板临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/palette' 别名来引入调色板文件,因此在绝大多数情况下你不需要修改该配置项。

    userStyleFile

    • 类型: string

    • 默认值:

      • css: '.vuepress/styles/index.css'
      • sass: '.vuepress/styles/index.scss'
      • less: '.vuepress/styles/index.less'
      • stylus: '.vuepress/styles/index.styl'
    • 详情:

      用户样式文件的路径,是针对源文件目录的相对路径。

      默认值依赖于 preset 配置项。

      该文件用于用户覆盖默认样式和添加额外样式,建议保持默认值作为约定的文件路径。

    tempStyleFile

    • 类型: string

    • 默认值:

      • css: 'styles/index.css'
      • sass: 'styles/index.scss'
      • less: 'styles/index.less'
      • stylus: 'styles/index.styl'
    • 详情:

      生成的样式临时文件的路径,是针对临时文件文件目录的相对路径。

      默认值依赖于 preset 配置项。

      你应该使用 '@vuepress/plugin-palette/style' 别名来引入样式文件,因此在绝大多数情况下你不需要修改该配置项。

    importCode

    • 类型: (filePath: string) => string

    • 默认值:

      • css: (filePath) => `@import '${filePath}';\n`
      • sass: (filePath) => `@forward 'file:///${filePath}';\n`
      • less: (filePath) => `@import '${filePath}';\n`
      • stylus: (filePath) => `@require '${filePath}';\n`
    • 详情:

      用于生成引入代码的函数。

      默认值依赖于 preset 配置项。

      该配置项用于生成 tempPaletteFiletempStyleFile ,在绝大多数情况下你不需要修改该配置项。

    + + + diff --git a/zh/plugins/prismjs.html b/zh/plugins/prismjs.html new file mode 100644 index 0000000000..02891c2264 --- /dev/null +++ b/zh/plugins/prismjs.html @@ -0,0 +1,47 @@ + + + + + + + + + prismjs | VuePress 生态系统 + + + + + +

    prismjs

    @vuepress/plugin-prismjs

    该插件使用 Prism.js在新窗口打开 来为 Markdown 代码块启用代码高亮。

    该插件已经集成到默认主题中。

    需要注意的是,该插件仅会给代码块添加 HTML 标记,而不会添加样式。当你在一个自定义主题中使用它时,可能需要自己选择并引入 Prism.js 样式主题。

    使用方法

    npm i -D @vuepress/plugin-prismjs@next
    +
    import { prismjsPlugin } from '@vuepress/plugin-prismjs'
    +
    +export default {
    +  plugins: [
    +    prismjsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    preloadLanguages

    • 类型: string[]

    • 默认值: ['markdown', 'jsdoc', 'yaml']

    • 详情:

      需要预加载的语言。

      默认情况下,语言会在解析 Markdown 文件时按需加载。

      然而, Prism.js 在动态加载语言时可能会遇到 一些潜在的问题在新窗口打开 。为了避免这些问题,你可以使用该配置项来预加载一些语言。

    + + + diff --git a/zh/plugins/pwa-popup.html b/zh/plugins/pwa-popup.html new file mode 100644 index 0000000000..61cb9e8cd7 --- /dev/null +++ b/zh/plugins/pwa-popup.html @@ -0,0 +1,76 @@ + + + + + + + + + pwa-popup | VuePress 生态系统 + + + + + +

    pwa-popup

    @vuepress/plugin-pwa-popup

    提供一个弹窗组件,允许用户手动刷新 PWA Service Worker 。

    该插件必须和 pwa 插件 一起使用,并且 skipWaiting 配置项不能设置为 true

    当新的 Service Worker 就绪时,会在页面右下角出现一个弹窗,询问用户是否需要激活处于 Waiting 状态的 Service Worker 。

    使用方法

    npm i -D @vuepress/plugin-pwa-popup@next
    +
    import { pwaPlugin } from '@vuepress/plugin-pwa'
    +import { pwaPopupPlugin } from '@vuepress/plugin-pwa-popup'
    +
    +export default {
    +  plugins: [
    +    pwaPlugin(),
    +    pwaPopupPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    locales

    • 类型: Record<string, { message: string, buttonText: string }>

    • 详情:

      弹窗在不同 locales 下的信息。

      如果没有指定该配置项,它会降级使用默认信息。

    • 示例:

    export default {
    +  plugins: [
    +    pwaPlugin(),
    +    pwaPopupPlugin({
    +      locales: {
    +        '/': {
    +          message: 'New content is available.',
    +          buttonText: 'Refresh',
    +        },
    +        '/zh/': {
    +          message: '发现新内容可用',
    +          buttonText: '刷新',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义弹窗的样式:

    :root {
    +  --pwa-popup-z-index: 10;
    +  --pwa-popup-text-color: #2c3e50;
    +  --pwa-popup-bg-color: #ffffff;
    +  --pwa-popup-border-color: #3eaf7c;
    +  --pwa-popup-shadow: 0 4px 16px var(--pwa-popup-border-color);
    +  --pwa-popup-btn-text-color: #ffffff;
    +  --pwa-popup-btn-bg-color: #3eaf7c;
    +  --pwa-popup-btn-hover-bg-color: #4abf8a;
    +}
    +
    + + + diff --git a/zh/plugins/pwa.html b/zh/plugins/pwa.html new file mode 100644 index 0000000000..75f7adff49 --- /dev/null +++ b/zh/plugins/pwa.html @@ -0,0 +1,104 @@ + + + + + + + + + pwa | VuePress 生态系统 + + + + + +

    pwa

    @vuepress/plugin-pwa

    使你的 VuePress 站点成为一个 渐进式 Web 应用 (PWA)在新窗口打开.

    该插件使用 workbox-build在新窗口打开 来生成 Service Worker 文件,并通过 register-service-worker在新窗口打开 来注册 Service Worker 。

    注意

    如果你启用过该插件,并想要禁用它,你可能需要 `@vuepress/plugin-remove-pwa 来移除现有的 Service Worker 。

    使用方法

    npm i -D @vuepress/plugin-pwa@next
    +
    import { pwaPlugin } from '@vuepress/plugin-pwa'
    +
    +export default {
    +  plugins: [
    +    pwaPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    Web App Manifests

    为了使你的网站符合 PWA 的要求,你需要创建一个 Web app manifests在新窗口打开 文件,并且为你的 PWA 设置图标、颜色等信息。

    你需要将你的 Manifest 文件和图标放置在 Public 目录 下。在下述的示例中,我们假设你正在使用默认的 Public 目录 .vuepress/public

    1. 创建 Manifest 文件

    通常是 .vuepress/public/manifest.webmanifest

    {
    +  "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"
    +    }
    +  ]
    +}
    +
    1. 生成 PWA 图标

    为了提高你的 PWA 的可用性,你需要生成一些图标,并将它们放置在 Public 目录下。

    确保图标的路径匹配 Manifest 文件中的 icons 字段:

    • .vuepress/public/images/icons/android-chrome-192x192.png
    • .vuepress/public/images/icons/android-chrome-384x384.png

    提示

    一些工具可以帮助你做这些事。比如 Favicon Generator在新窗口打开 可以帮助你生成图片以及一个 Manifest 文件样例。

    1. 设置 Head 中的标签

    你还需要通过 head 配置项来设置一些标签,用来 部署你的 Manifest 文件在新窗口打开

    export default {
    +  head: [
    +    ['link', { rel: 'manifest', href: '/manifest.webmanifest' }],
    +    ['meta', { name: 'theme-color', content: '#3eaf7c' }],
    +    // ...其他标签
    +  ],
    +}
    +

    配置项

    该插件的配置项可以接收 workbox-build 中 generateSW 方法在新窗口打开 除了 globDirectoryswDest 以外的所有参数。

    比如,你可以设置 skipWaiting: true ,这将在新的 Service Worker 就绪之后立即激活它:

    export default {
    +  plugins: [
    +    pwaPlugin({
    +      skipWaiting: true,
    +    }),
    +  ],
    +}
    +

    但是如果你不设置 skipWaiting 或设置为 false ,你就需要手动激活新的 Service Worker 。

    • 对于用户,你可以配合我们提供的 pwa-popup 插件一起使用。
    • 对于开发者,你可以使用该插件提供的 Composition API 来控制 Service Worker 的行为。

    serviceWorkerFilename

    • 类型: string

    • 默认值: 'service-worker.js'

    • 详情:

      生成的 Service Worker 文件路径,该路径是 dest 目录的相对路径。

      Service Worker 文件只会在 build 模式下生成。

    Composition API

    usePwaEvent

    import { usePwaEvent } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('ready', (registration) => {
    +      console.log('Service worker 已经生效。')
    +    })
    +  },
    +}
    +

    useSkipWaiting

    • 参数:
    参数类型描述
    registrationServiceWorkerRegistration你想要激活的 Service Worker 的 Registration
    import { usePwaEvent, useSkipWaiting } from '@vuepress/plugin-pwa/client'
    +
    +export default {
    +  setup() {
    +    const event = usePwaEvent()
    +    event.on('updated', (registration) => {
    +      console.log('在 Waiting 状态的 Service Worker 已经就绪。')
    +      // 激活 Waiting 状态的 Service Worker
    +      useSkipWaiting(registration)
    +    })
    +  },
    +}
    +
    + + + diff --git a/zh/plugins/reading-time.html b/zh/plugins/reading-time.html new file mode 100644 index 0000000000..a792d4e88d --- /dev/null +++ b/zh/plugins/reading-time.html @@ -0,0 +1,131 @@ + + + + + + + + + reading-time | VuePress 生态系统 + + + + + +

    reading-time

    @vuepress/plugin-reading-time

    此插件会为每个页面生成字数统计与预计阅读时间。

    使用方法

    npm i -D @vuepress/plugin-reading-time@next
    +
    import { readingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default {
    +  plugins: [
    +    readingTimePlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    插件会将相关信息注入到页面数据的 readingTime,其中:

    • readingTime.minutes:为预计阅读时间(分钟)number
    • readingTime.words:字数统计,number

    在 Node 侧获取数据

    对于任何页面,你可以从 page.data.readingTime 获取预计阅读时间与字数统计:

    page.data.readingTime // { minutes: 3.2, words: 934 }
    +

    你可以在 extendsPage 以及其他生命周期获取它做进一步处理:

    export default {
    +  // ...
    +  extendsPage: (page) => {
    +    page.data.readingTime // { minutes: 3.2, words: 934 }
    +  },
    +
    +  onInitialized: (app) => {
    +    app.pages.map((page) => {
    +      page.data.readingTime // { minutes: 3.2, words: 934 }
    +    })
    +  },
    +}
    +

    在客户端侧获取数据

    你可以从 @vuepress/plugin-reading-time/client 导入 useReadingTimeDatauseReadingTimeLocale 来获取当前页面的阅读时间数据和语言环境数据:

    <script setup lang="ts">
    +import {
    +  useReadingTimeData,
    +  useReadingTimeLocale,
    +} from '@vuepress/plugin-reading-time/client'
    +
    +const readingTimeData = useReadingTimeData() // { minutes: 1.1, words: 100 }
    +const readingTimeLocale = useReadingTimeLocale() // { time: "1 分钟", words: "100 字" }
    +</script>
    +

    选项

    wordPerMinute

    • 类型:number
    • 默认值:300
    • 详情: 每分钟阅读字数

    locales

    • 类型:ReadingTimeLocaleConfig

      interface ReadingTimeLocaleData {
      +  /**
      +   * 字数模板,模板中 `$word` 会被自动替换为字数
      +   */
      +  word: string
      +
      +  /**
      +   * 小于一分钟文字
      +   */
      +  less1Minute: string
      +
      +  /**
      +   * 时间模板
      +   */
      +  time: string
      +}
      +
      +interface ReadingTimeLocaleConfig {
      +  [localePath: string]: ReadingTimeLocaleData
      +}
      +
    • 必填:否

    • 详情:

      阅读时间插件的国际化配置。

    内置支持语言
    • 简体中文 (zh-CN)
    • 繁体中文 (zh-TW)
    • 英文(美国) (en-US)
    • 德语 (de-DE)
    • 德语(澳大利亚) (de-AT)
    • 俄语 (ru-RU)
    • 乌克兰语 (uk-UA)
    • 越南语 (vi-VN)
    • 葡萄牙语(巴西) (pt-BR)
    • 波兰语 (pl-PL)
    • 法语 (fr-FR)
    • 西班牙语 (es-ES)
    • 斯洛伐克 (sk-SK)
    • 日语 (ja-JP)
    • 土耳其语 (tr-TR)
    • 韩语 (ko-KR)
    • 芬兰语 (fi-FI)
    • 印尼语 (id-ID)
    • 荷兰语 (nl-NL)

    客户端 API

    你可以从 @vuepress/plugin-reading-time/client 导入并使用这些 API:

    即使插件被禁用,这些 API 也不会抛出错误。

    useReadingTimeData

    interface ReadingTime {
    +  /** 分钟为单位的预计阅读时长 */
    +  minutes: number
    +  /** 内容的字数 */
    +  words: number
    +}
    +
    +const useReadingTimeData: () => ComputedRef<ReadingTime | null>
    +

    当插件被禁用时会返回 null

    useReadingTimeLocale

    interface ReadingTimeLocale {
    +  /** 当前语言的预计阅读时间 */
    +  time: string
    +  /** 当前语言的字数文字 */
    +  words: string
    +}
    +
    +const useReadingTimeLocale: () => ComputedRef<ReadingTimeLocale>
    +

    高级使用

    由于此插件主要面向插件和主题开发者,所以提供了 "使用 API":

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +

    为什么你应该使用 "使用 API"

    1. 当你多次注册一个插件时,vuepress 会给你一个警告,告诉你只有第一个插件会生效。useReadingTimePlugin 会自动检测插件是否已经注册,避免多次注册。

    2. 如果你在 extendsPage 生命周期访问阅读时间数据,那么 @vuepress/plugin-reading-time 必须在你的主题或插件之前被调用,否则你会得到未定义的 page.data.readingTimeuseReadingTimePlugin 确保了 @vuepress/plugin-reading-time 在你的主题或插件之前被调用。

    我们也提供了一个 removeReadingTimePlugin api 来移除插件。你可以使用它来确保你的调用生效或清除插件:

    import { useReadingTimePlugin } from '@vuepress/plugin-reading-time'
    +
    +export default (options) => (app) => {
    +  // 这会移除任何当前存在的阅读时间插件
    +  removeReadingTimePlugin(app)
    +
    +  // 所以这会生效,即使之前已经注册了一个阅读时间插件
    +  useReadingTimePlugin(app, {
    +    // 你的选项
    +  })
    +
    +  return {
    +    name: 'vuepress-plugin-xxx', // or vuepress-theme-xxx
    +  }
    +}
    +
    + + + diff --git a/zh/plugins/redirect.html b/zh/plugins/redirect.html new file mode 100644 index 0000000000..5ed3cdaf15 --- /dev/null +++ b/zh/plugins/redirect.html @@ -0,0 +1,107 @@ + + + + + + + + + redirect | VuePress 生态系统 + + + + + +

    redirect

    @vuepress/plugin-rtl

    此插件提供页面与整站重定向功能。

    使用方法

    npm i -D @vuepress/plugin-redirect@next
    +
    import { redirectPlugin } from '@vuepress/plugin-redirect'
    +
    +export default {
    +  plugins: [
    +    redirectPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    设置重定向

    如果你改动了已有页面的地址,你可以在 Frontmatter 中使用 redirectFrom 选项设置重定向到此页面的地址,这样可以保证用户在访问旧链接时重定向到新的地址。

    如果你需要将已有的页面重定向到新的页面,可以在 Frontmatter 中使用 redirectTo 选项设置需要重定向到的地址。这样该页面会在访问时重定向到新的地址。

    你还可以通过插件选项中的 config 设置一个重定向映射,详见 config

    自动多语言

    插件可以根据用户的语言首选项,自动将无多语言链接重定向到用户需要的多语言页面。为了实现这一点,你需要留空默认的语言目录 (/),并在插件选项中设置 autoLocale: true。插件会自动根据用户语言跳转到对应的语言页面。

    也就是你需要设置以下目录结构:

    .
    +├── en
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +├── zh
    +│   ├── ...
    +│   ├── page.md
    +│   └── README.md
    +└── other_languages
    +    ├── ...
    +    ├── page.md
    +    └── README.md
    +

    并将主题选项的 locales 设置为:

    export default {
    +  locales: {
    +    '/en/': {
    +      lang: 'en-US',
    +      // ...
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +      // ...
    +    },
    +    // other languages
    +  },
    +  // ...
    +}
    +

    这样当用户访问 //page.html 时,他们会自动根据当前浏览器语言重定向到 /en/ /en/page.html/zh/ /zh/page.html

    自定义回退行为

    有些时候,用户可能会在系统设置中添加多个语言。默认情况下,在站点支持首选语言,但首选语言不存在相应页面时,插件会尝试匹配用户设置的备用语言。

    如果不需要回退到用户备用语言,而直接匹配用户首选语言,请在插件选项中设置 localeFallback: false

    自定义缺失行为

    有些时候,当用户访问一个页面时,文档尚未包含用户需要的语言版本 (一个普遍的情况是当前页面尚未完成相关语言的本地化),这样插件需要做出默认行为,你可以通过插件选项中的 defaultBehavior 定制它:

    • "defaultLocale": 重定向到默认语言或首个可用语言页面 (默认行为)
    • "homepage": 重定向到当前语言的主页 (仅在文档包含用户语言时可用)
    • "404": 重定向到当前语言的 404 页 (仅在文档包含用户语言时可用)

    自定义默认路径

    你可以通过设置插件选项中的 defaultLocale 来自定义默认路径。默认情况下,插件会使用 locales 中的第一个键名作为默认路径。

    自动切换语言

    插件支持在多语言文档中,自动根据用户语言首选项,将链接切换到用户需要的多语言页面。为了实现这一点,你需要在插件选项中设置 switchLocale,它可以是以下两个值:

    • direct: 直接切换到用户语言首选项页面,而不询问
    • modal: 在用户语言首选项与当前页面语言不同时,弹出一个对话框询问用户是否切换语言

    自定义多语言配置

    默认情况下,插件会从站点的多语言配置 locales 选项中,读取 语言路径lang 生成多语言配置。有些时候,你可能希望多个语言命中同一个路径,这种情况下,你应该设置插件的 localeConfig 选项。

    比如,你可能希望所有英文用户都匹配到 /en/,并将繁体中文用户匹配到 /zh/ 中,那么你可以设置:

    redirect({
    +  localeConfig: {
    +    '/en/': ['en-US', 'en-UK', 'en'],
    +    '/zh/': ['zh-CN', 'zh-TW', 'zh'],
    +  },
    +})
    +

    重定向站点

    有时你可能会更改 base 或为你的站点使用新域名,因此你可能希望原始站点自动重定向到新站点。

    为了解决这个问题,插件提供了 vp-redirect 脚手架。

    使用:
    +  $ vp-redirect generate [源文件夹]
    +
    +Options:
    +  --hostname <hostname>  重定向到的域名 (例如: https://new.example.com/) (默认: /)
    +  -c, --config <config>  设置配置文件路径
    +  -o, --output <output>  设置输出目录 (默认: .vuepress/redirect)
    +  --cache <cache>        设置缓存文件的目录
    +  -t, --temp <temp>      设置临时文件的目录
    +  --clean-cache          生成前清理缓存文件
    +  --clean-temp           生成前清理临时文件
    +  -h, --help             显示此消息
    +

    你需要传入 VuePress 项目源目录并设置 hostname 选项。重定向助手脚手架将初始化你的 VuePress 项目以获取页面,然后在输出目录生成重定向 html 文件。

    默认情况下,插件将输出到源文件夹下的 .vuepress/redirect 目录。你应该将其上传到你的原始站点以提供重定向。

    选项

    config

    • 类型:Record<string, string> | ((app: App) => Record<string, string>)

    • 详情

      页面重定向映射。

      可直接传入对象或传入参数为 App 的函数返回值一个对象。

      每个键名必须是一个绝对路径,代表重定向的源页面地址。

      每个键值是重定向的目标地址,可以是绝对路径或完整路径。

    • 示例:

      当 base 为 /base/时:

      • /base/foo.html 重定向到 /base/bar.html
      • /base/baz.html 重定向到 https://example.com/qux.html
      redirect({
      +  config: {
      +    '/foo.html': '/bar.html',
      +    '/baz.html': 'https://example.com/qux.html',
      +  },
      +})
      +

      将 post 文件夹的路径重定向到 posts 文件夹

      redirect({
      +  hostname: 'https://example.com',
      +  config: (app) =>
      +    Object.fromEntries(
      +      app.pages
      +        .filter(({ path }) => path.startsWith('/posts/'))
      +        .map(({ path }) => [path.replace(/^\/posts\//, '/post/'), path]),
      +    ),
      +})
      +

    autoLocale

    switchLocale

    • 类型:"direct" | "modal" | false

    • 默认值: false

    • 详情:

      是否根据用户偏好切换到新的语言环境。

      • "direct": 直接重定向到新的语言环境而不询问
      • "modal": 显示一个模式让用户选择是否切换到新的语言环境

    localeConfig

    • 类型:Record<string, string | string[]>
    • 详情:多语言语言配置

    localeFallback

    • 类型:boolean
    • 默认值: true
    • 详情:是否回退到用户定义的其他语言

    defaultBehavior

    • 类型:"defaultLocale" | "homepage" | "404"
    • 默认值: "defaultLocale"
    • 详情:当前链接没有可用的语言版本时的行为

    defaultLocale

    • 类型:string
    • 默认值: 首个语言路径
    • 详情:默认语言路径

    Frontmatter

    redirectFrom

    • 类型:string | string[]
    • 详情:重定向到该页面的地址。

    redirectTo

    • 类型:string
    • 详情:该页面重定向到的地址。
    + + + diff --git a/zh/plugins/register-components.html b/zh/plugins/register-components.html new file mode 100644 index 0000000000..c6a74569d9 --- /dev/null +++ b/zh/plugins/register-components.html @@ -0,0 +1,85 @@ + + + + + + + + + register-components | VuePress 生态系统 + + + + + +

    register-components

    @vuepress/plugin-register-components

    根据组件文件或目录自动注册 Vue 组件。

    使用方法

    npm i -D @vuepress/plugin-register-components@next
    +
    import { registerComponentsPlugin } from '@vuepress/plugin-register-components'
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    components

    • 类型: Record<string, string>

    • 默认值: {}

    • 详情:

      一个定义了组件名称和其对应文件路径的对象。

      键会被用作组件名称,值是组件文件的绝对路径。

      如果该配置项中的组件名称和 componentsDir 配置项发生冲突,那么该配置项会有更高的优先级。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      components: {
    +        FooBar: path.resolve(__dirname, './components/FooBar.vue'),
    +      },
    +    }),
    +  ],
    +}
    +

    componentsDir

    • 类型: string | null

    • 默认值: null

    • 详情:

      组件目录的绝对路径。

      该目录下匹配 componentsPatterns 的文件会被自动注册为 Vue 组件。

    • 示例:

    import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default {
    +  plugins: [
    +    registerComponentsPlugin({
    +      componentsDir: path.resolve(__dirname, './components'),
    +    }),
    +  ],
    +}
    +

    组件目录:

    components
    +├─ FooBar.vue
    +└─ Baz.vue
    +

    组件会像这样被注册:

    import { defineAsyncComponent } from 'vue'
    +
    +app.component(
    +  'FooBar',
    +  defineAsyncComponent(() => import('/path/to/components/FooBar.vue')),
    +)
    +
    +app.component(
    +  'Baz',
    +  defineAsyncComponent(() => import('/path/to/components/Baz.vue')),
    +)
    +

    componentsPatterns

    getComponentName

    • 类型: (filename: string) => string

    • 默认值: (filename) => path.trimExt(filename.replace(/\/|\\/g, '-'))

    • 详情:

      用于从文件名获取对应组件名称的函数。

      它只会对 componentsDir 目录下匹配了 componentsPatterns 的文件生效。

      注意,这里的 filename 是相对于 componentsPatterns 目录的文件路径。

    + + + diff --git a/zh/plugins/remove-pwa.html b/zh/plugins/remove-pwa.html new file mode 100644 index 0000000000..1999df635b --- /dev/null +++ b/zh/plugins/remove-pwa.html @@ -0,0 +1,47 @@ + + + + + + + + + remove-pwa | VuePress 生态系统 + + + + + +

    remove-pwa

    @vuepress/plugin-remove-pwa

    此插件从你的 VuePress 站点中删除任何相关的 Service Worker,因此如果你在启用后任何 PWA 插件后移除它们,用户仍然可以获得更新。

    如果你启用过 PWA,为什么需要这个插件?

    PWA 插件,如 @vuepress/plugin-pwa 将 Service Worker 注册到你的站点,这将缓存你的站点并使其离线可用。

    但是,如果你删除 PWA 插件,先前的 Service Worker 仍将在那里,但它们永远无法获得更新,因为他们永远无法找到要更新的新 Service Worker。 因此,用户将继续使用你网站的旧版本。

    要解决这个问题:

    1. 一个新的内容为空的 Service Worker 需要生成在原位置。

    2. 新的 Service Worker 应该尝试删除旧 Service Worker 缓存的内容,然后它应该注销自己。

    Usage

    npm i -D @vuepress/plugin-remove-pwa@next
    +
    import { removePwaPlugin } from '@vuepress/plugin-remove-pwa'
    +
    +export default {
    +  plugins: [
    +    removePwaPlugin({
    +      // options
    +    }),
    +  ],
    +}
    +

    Config

    cachePrefix

    • 类型:string
    • 默认值:'workbox'
    • 详情:Service worker 的缓存前缀。

    swLocation

    • 类型: string
    • 默认值:'service-worker.js'
    • 详情:旧 Service Worker 的位置。
    + + + diff --git a/zh/plugins/rtl.html b/zh/plugins/rtl.html new file mode 100644 index 0000000000..cb3983fbea --- /dev/null +++ b/zh/plugins/rtl.html @@ -0,0 +1,53 @@ + + + + + + + + + rtl | VuePress 生态系统 + + + + + +

    rtl

    @vuepress/plugin-rtl

    此插件会在配置的语言上设置 rtl 方向。

    使用方法

    npm i -D @vuepress/plugin-rtl@next
    +
    import { rtlPlugin } from '@vuepress/plugin-rtl'
    +
    +export default {
    +  plugins: [
    +    rtlPlugin({
    +      // 配置项
    +      locales: ['/ar/'],
    +    }),
    +  ],
    +}
    +

    选项

    locales

    • 类型:string[]
    • 默认值:['/']
    • 详情: 开启 RTL 布局的多语言路径。

    selector

    • 类型:SelectorOptions

      interface SelectorOptions {
      +  [element: string]: {
      +    [attrs: string]: string
      +  }
      +}
      +
    • 默认值:{ 'html': { dir: 'rtl' } }

    • 详情:

      开启 RTL 的选择器。

      默认设置意味着在 RTL 多语言中,html 元素的 dir 属性将被设置为 rtl

    + + + diff --git a/zh/plugins/search.html b/zh/plugins/search.html new file mode 100644 index 0000000000..ac4b819e2d --- /dev/null +++ b/zh/plugins/search.html @@ -0,0 +1,111 @@ + + + + + + + + + search | VuePress 生态系统 + + + + + +

    search

    @vuepress/plugin-search

    为你的文档网站提供本地搜索能力。

    提示

    当你正确配置该插件后,默认主题会把搜索框添加到导航栏。

    该插件不一定能在其他主题中直接使用,因此你应参考主题本身的文档来获取更多信息。

    使用方法

    npm i -D @vuepress/plugin-search@next
    +
    import { searchPlugin } from '@vuepress/plugin-search'
    +
    +export default {
    +  plugins: [
    +    searchPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    本地搜索索引

    该插件会根据你的页面,在本地生成搜索索引,然后在用户访问站点时加载搜索索引文件。换句话说,这是一个轻量级的内置搜索能力,不会进行任何外部请求。

    然而,当你的站点包含大量页面时,搜索索引文件也会变得非常大,它可能会拖慢你的页面加载速度。在这种情况下,我们建议你使用更成熟的解决方案 - docsearch

    配置项

    locales

    • 类型: Record<string, { placeholder: string }>

    • 详情:

      搜索框在不同 locales 下的文字。

      如果没有指定该配置项,它会降级使用默认文字。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      locales: {
    +        '/': {
    +          placeholder: 'Search',
    +        },
    +        '/zh/': {
    +          placeholder: '搜索',
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    hotKeys

    • 类型: (string | HotKeyOptions)[]
    export interface HotKeyOptions {
    +    /**
    +     * Value of `event.key` to trigger the hot key
    +     */
    +    key: string;
    +    /**
    +     * Whether to press `event.altKey` at the same time
    +     *
    +     * @default false
    +     */
    +    alt?: boolean;
    +    /**
    +     * Whether to press `event.ctrlKey` at the same time
    +     *
    +     * @default false
    +     */
    +    ctrl?: boolean;
    +    /**
    +     * Whether to press `event.shiftKey` at the same time
    +     *
    +     * @default false
    +     */
    +    shift?: boolean;
    +}
    +
    • 默认值: ['s', '/']

    • 详情:

      指定热键的 event.key在新窗口打开

      当按下热键时,搜索框会被聚焦。

      将该配置项设为空数组可以禁用热键功能。

    maxSuggestions

    • 类型: number

    • 默认值: 5

    • 详情:

      指定搜索结果的最大条数。

    isSearchable

    • 类型: (page: Page) => boolean

    • 默认值: () => true

    • 详情:

      一个函数,用于判断一个页面是否应该被包含在搜索索引中。

      • 返回 true 来包含该页面。
      • 返回 false 来排除该页面。
    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 排除首页
    +      isSearchable: (page) => page.path !== '/',
    +    }),
    +  ],
    +}
    +

    getExtraFields

    • 类型: (page: Page) => string[]

    • 默认值: () => []

    • 详情:

      一个函数,用于在页面的搜索索引中添加额外字段。

      默认情况下,该插件会将页面标题和小标题作为搜索索引。该配置项可以帮助你添加更多的可搜索字段。

    • 示例:

    export default {
    +  plugins: [
    +    searchPlugin({
    +      // 允许搜索 Frontmatter 中的 `tags`
    +      getExtraFields: (page) => page.frontmatter.tags ?? [],
    +    }),
    +  ],
    +}
    +

    样式

    你可以通过 CSS 变量来自定义搜索框的样式:

    :root {
    +  --search-bg-color: #ffffff;
    +  --search-accent-color: #3eaf7c;
    +  --search-text-color: #2c3e50;
    +  --search-border-color: #eaecef;
    +  --search-item-text-color: #5d81a5;
    +  --search-item-focus-bg-color: #f3f4f5;
    +  --search-input-width: 8rem;
    +  --search-result-width: 20rem;
    +}
    +

    组件

    • 详情:

      该插件会全局注册一个 <SearchBox /> 组件,你可以不传入任何 Props 来使用它。

      将该组件放置在你想要显示搜索框的地方。例如,默认主题将这个组件放在了导航栏的末尾。

    提示

    该组件主要用于主题开发。在大多数情况下你不需要直接使用该组件。

    + + + diff --git a/zh/plugins/seo/config.html b/zh/plugins/seo/config.html new file mode 100644 index 0000000000..0eb9880e26 --- /dev/null +++ b/zh/plugins/seo/config.html @@ -0,0 +1,81 @@ + + + + + + + + + 选项 | VuePress 生态系统 + + + + + +

    选项

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      部署域名

    author

    • 类型:Author

      type AuthorName = string
      +
      +interface AuthorInfo {
      +  /**
      +   * 作者姓名
      +   */
      +  name: string
      +
      +  /**
      +   * 作者网站
      +   */
      +  url?: string
      +
      +  /**
      +   * 作者 Email
      +   */
      +  email?: string
      +}
      +
      +type Author = AuthorName | AuthorName[] | AuthorInfo | AuthorInfo[]
      +
    • 详情:

      默认作者

    autoDescription

    • 类型:boolean

    • 默认值:true

    • 详情:

      是否自动生成描述

    canonical

    • 类型:string | ((page: Page) => string | null)

    • 详情:

      首选链接

    fallBackImage

    • 类型:string

    • 详情:

      当找不到图片时的回退图片链接

    restrictions

    • 类型:string

    • 详情:

      内容的年龄分级,格式为 [int]+,如 "13+"

    twitterID

    • 类型:string

    • 详情:

      你的 twitter 用户名

    isArticle

    • 类型:(page: Page) => boolean

    • 详情:

      你可以使用此选项判断一个页面是否是文章。

    ogp

    • 类型:

      function ogp(
      +  /** 插件推断的 OGP 信息 */
      +  ogp: SeoContent,
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): SeoContent
      +
    • 详情:

      自定义 OGP 生成器

      你可以使用此选项来注入新的或覆盖掉默认生成的 OGP 标签。

    jsonLd

    • 类型:

      function jsonLd(
      +  /** 由插件推断出的 JSON-LD 对象 */
      +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): ArticleSchema | BlogPostingSchema | WebPageSchema
      +
    • 详情:

      自定义 JSON-LD 生成器

      你可以使用此选项来注入新的或覆盖掉默认生成的 JSON-LD 标签。

    customHead

    • 类型:

      function customHead(
      +  /** head 标签配置 */
      +  head: HeadConfig[],
      +  /** 页面对象 */
      +  page: Page,
      +  /** VuePress App */
      +  app: App,
      +): void
      +
    • 详情:

      你可以使用此选项来直接注入任意格式的标签到 <head>

    + + + diff --git a/zh/plugins/seo/guide.html b/zh/plugins/seo/guide.html new file mode 100644 index 0000000000..3ae72dab4f --- /dev/null +++ b/zh/plugins/seo/guide.html @@ -0,0 +1,73 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    本插件会通过向网站 <head> 注入标签,让你的网站完全支持 开放内容协议 OGP在新窗口打开JSON-LD 1.1在新窗口打开,以全面增强站点的搜索引擎优化性。

    开箱即用

    插件开箱即用,在不做任何配置的情况下,会尽可能通过页面内容,提取对应的信息补全 OGP 与 JSON-LD 所需的必要标签。

    默认情况下,插件会读取站点配置、主题配置与页面的 frontmatter 来尽可能自动生成。诸如站点名称,页面标题,页面类型,写作日期,最后更新日期,文章标签均会自动生成。

    默认的 OGP 生成逻辑

    属性名称
    og:urloptions.hostname + path
    og:site_namesiteConfig.title
    og:titlepage.title
    og:descriptionpage.frontmatter.description || 自动生成 (当插件选项中的 autoDescriptiontrue 时)
    og:type"article"
    og:imageoptions.hostname + page.frontmatter.image || 页面的第一张图片|| 插件选项的 fallbackImage
    og:updated_timepage.git.updatedTime
    og:localepage.lang
    og:locale:alternatesiteData.locales 包含的其他语言
    twitter:card"summary_large_image" (仅在找到图片时)
    twitter:image:altpage.title (仅在找到图片时)
    article:authorpage.frontmatter.author || options.author
    article:tagpage.frontmatter.tags || page.frontmatter.tag
    article:published_timepage.frontmatter.date || page.git.createdTime
    article:modified_timepage.git.updatedTime

    默认的 JSON-LD 生成逻辑

    属性名
    @context"https://schema.org"
    @type"NewsArticle"
    headlinepage.title
    image页面中的图片|| options.hostname + page.frontmatter.image
    datePublishedpage.frontmatter.date || page.git.createdTime
    dateModifiedpage.git.updatedTime
    authorpage.frontmatter.author || options.author

    直接添加 head 标签

    你可以在页面的 frontmatter 中配置 head 选项,自主添加特定标签到页面 <head> 以增强 SEO。

    如:

    ---
    +head:
    +  - - meta
    +    - name: keywords
    +      content: SEO plugin
    +---
    +

    会自动注入 <meta name="keywords" content="SEO plugin" />

    自定义生成过程

    本插件也支持你完全控制生成逻辑。

    页面类型

    对于大多数页面,基本只有文章和网页两种类型,所以插件提供了 isArticle 选项让你提供辨别文章的逻辑。

    选项接受一个 (page: Page) => boolean 格式的函数,默认情况下从 Markdown 文件生成的非主页页面都会被视为文章。

    提示

    如果某个网页的确符合图书、音乐之类的“冷门”类型,你可以通过设置下方三个选项处理它们。

    OGP

    你可以使用插件选项的 ogp 传入一个函数来按照你的需要修改默认 OGP 对象并返回。

    function ogp(
    +  /** 插件推断的 OGP 信息 */
    +  ogp: SeoContent,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): SeoContent
    +

    详细的参数结构详见 配置

    比如你在使用某个第三方主题,并按照主题要求为每篇文章在 Front Matter 中设置了 banner,那你可以传入这样的 ogp:

    seoPlugin({
    +  ogp: (ogp, page) => ({
    +    ...ogp,
    +    'og:image': page.frontmatter.banner || ogp['og:image'],
    +  }),
    +})
    +

    JSON-LD

    同 OGP,你可以使用插件选项的 jsonLd 传入一个函数来按照你的需要修改默认 JSON-LD 对象并返回。

    function jsonLd(
    +  /** 由插件推断出的 JSON-LD 对象 */
    +  jsonLD: ArticleSchema | BlogPostingSchema | WebPageSchema,
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): ArticleSchema | BlogPostingSchema | WebPageSchema
    +

    规范链接

    如果你将内容部署到不同的站点,或不同 URL 下的相同内容,你可能需要设置 canonical 选项为你的页面提供 “规范链接”。 你可以设置一个字符串,这样它会附加在页面路由链接之前,或者添加一个自定义函数 (page: Page) => string | null 返回规范链接。

    例子

    如果你的站点部署在 example.com 的 docs 文件夹下,但同时在下列网址中可用:

    • http://example.com/docs/xxx
    • https://example.com/docs/xxx
    • http://www.example.com/docs/xxx
    • https://www.example.com/docs/xxx (首选)

    要让搜索引擎结果始终是首选,你可能需要将 canonical 设置为 https://www.example.com/docs/,以便搜索引擎知道首选第四个 URL 作为索引结果。

    自定义 head 标签

    有些时候你可能需要符合其他协议或按照其他搜索引擎提供的格式提供对应的 SEO 标签,此时你可以使用 customHead 选项,其类型为:

    function customHead(
    +  /** head 标签配置 */
    +  head: HeadConfig[],
    +  /** 页面对象 */
    +  page: Page,
    +  /** VuePress App */
    +  app: App,
    +): void
    +

    你应该直接修改传入的 head 参数。

    SEO 介绍

    搜索引擎优化 (Search Engine Optimization),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。 所谓“针对搜索引擎作最优化的处理”,是指为了要让网站更容易被搜索引擎接受。搜索引擎会将网站彼此间的内容做一些相关性的资料比对,然后再由浏览器将这些内容以最快速且接近最完整的方式,呈现给搜索者。搜索引擎优化就是通过搜索引擎的规则进行优化,为用户打造更好的用户体验,最终的目的就是做好用户体验。

    相关文档

    相关工具

    你可以使用 Google 富媒体结构测试工具在新窗口打开 测试本站点。

    + + + diff --git a/zh/plugins/seo/index.html b/zh/plugins/seo/index.html new file mode 100644 index 0000000000..a2f6ee1542 --- /dev/null +++ b/zh/plugins/seo/index.html @@ -0,0 +1,47 @@ + + + + + + + + + seo | VuePress 生态系统 + + + + + +

    seo

    @vuepress/plugin-seo

    使用

    npm i -D @vuepress/plugin-seo@next
    +
    import { seoPlugin } from '@vuepress/plugin-seo'
    +
    +export default {
    +  plugins: [
    +    seoPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/shiki.html b/zh/plugins/shiki.html new file mode 100644 index 0000000000..219bffe5f2 --- /dev/null +++ b/zh/plugins/shiki.html @@ -0,0 +1,48 @@ + + + + + + + + + shiki | VuePress 生态系统 + + + + + +

    shiki

    @vuepress/plugin-shiki

    该插件使用 Shiki在新窗口打开 (Shikiji在新窗口打开) 来为 Markdown 代码块启用代码高亮。

    提示

    Shiki在新窗口打开 是 VSCode 正在使用的代码高亮器。它具有更高的保真度,但可能会比 Prism.js在新窗口打开 要慢一些,特别是在有大量代码块需要处理的时候。

    你可以考虑在 dev 模式下禁用该插件来获取更好的开发体验。

    使用方法

    npm i -D @vuepress/plugin-shiki@next
    +
    import { shikiPlugin } from '@vuepress/plugin-shiki'
    +
    +export default {
    +  plugins: [
    +    shikiPlugin({
    +      // 配置项
    +      langs: ['ts', 'json', 'vue', 'md', 'bash', 'diff'],
    +    }),
    +  ],
    +}
    +

    配置项

    langs

    • 类型: ShikiLang[]

    • 详情:

      Shikiji 要解析的代码块的语言。

      该配置项会被传递到 Shikiji 的 getHighlighter() 方法中。

      你需要明确传入所有你使用的语言列表,否则 Shikiji 将不会加载任何语言。

    • 参考:

    theme

    • 类型: ShikiTheme

    • 默认值: 'nord'

    • 详情:

      Shikiji 的主题。

      该配置项会被传递到 Shikiji 的 codeToHtml() 方法中。

    • 参考:

    themes

    • 类型:Record<'dark' | 'light', ShikiTheme>

    • 详情:

      Shikiji 的暗黑和明亮模式双主题。

      该配置项会被传递到 Shikiji 的 codeToHtml() 方法中。

    • 参考:

    + + + diff --git a/zh/plugins/sitemap/config.html b/zh/plugins/sitemap/config.html new file mode 100644 index 0000000000..c71d55aa2e --- /dev/null +++ b/zh/plugins/sitemap/config.html @@ -0,0 +1,37 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +

    配置

    hostname

    • 类型:string

    • 必填:是

    • 详情:

      当前网站部署到的域名,插件需要此选项才能工作。

    extraUrls

    • 类型:string[]

    • 详情:

      需要额外包含的网址。

      如果你有一些不包含在 VuePress 路由中的链接 (如: 存放在 public 文件夹下的页面或其他插件或工具直接生成的页面),你可能需要设置此项。

    • 示例:['/about.html', '/api/']

    excludePaths

    • 类型:string[]

    • 默认值:['/404.html']

    • 详情:

      不需要收录的页面路径,请以绝对路径开头。

      默认情况下 VuePress 自动生成的所有路径 (除 404 页) 都会被添加进 Sitemap。

    devServer

    • 类型:boolean

    • 默认值:false

    • 详情:

      是否在开发服务器中启用

    提示

    由于性能原因,我们不提供热更新。重启开发服务器以同步你的变更。

    devHostname

    • 类型:string

    • 默认值:"http://localhost:${port}"

    • 详情:

      开发服务器使用的主机名

    sitemapFilename

    • 类型:string

    • 默认值:"sitemap.xml"

    • 详情:

      输出的文件名,相对于输出目录。

    sitemapXSLFilename

    • 类型:string

    • 默认值:"sitemap.xsl"

    • 详情:

      输出的 xsl 文件名,相对于输出目录。

    sitemapXSLTemplate

    • 类型:string

    • 默认值:"@vuepress-plugin/sitemap/templates/sitemap.xsl"

    • 详情:

      用作模板的 XSL 文件内容

    changefreq

    • 类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    • 默认值:"daily"

    • 详情:

      页面默认更新频率,会被 Frontmatter 中的 changefreq 选项覆盖。

    priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    modifyTimeGetter

    • 类型:(page: Page, app: App) => string

    • 详情:

      最后修改事件的获得器,需要返回一个 ISO 字符形式的时间,默认会自动通过 Git 插件生成。

    + + + diff --git a/zh/plugins/sitemap/frontmatter.html b/zh/plugins/sitemap/frontmatter.html new file mode 100644 index 0000000000..c23e34ec95 --- /dev/null +++ b/zh/plugins/sitemap/frontmatter.html @@ -0,0 +1,37 @@ + + + + + + + + + Frontmatter | VuePress 生态系统 + + + + + +

    Frontmatter

    sitemap

    • 类型:SitemapFrontmatterOptions | false

    • 详情:

      false 表示将页面排除在 sitemap 之外。

    sitemap.changefreq

    • 类型:"always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"

    • 默认值:"daily"

    • 详情:

      页面默认更新频率。它会覆盖插件选项中的 changefreq 选项。

    sitemap.priority

    • 类型:number

    • 默认值:0.5

    • 详情:

      页面优先级,范围 01

    + + + diff --git a/zh/plugins/sitemap/guide.html b/zh/plugins/sitemap/guide.html new file mode 100644 index 0000000000..9457f57e71 --- /dev/null +++ b/zh/plugins/sitemap/guide.html @@ -0,0 +1,45 @@ + + + + + + + + + 指南 | VuePress 生态系统 + + + + + +

    指南

    本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。如果你想在开发服务器中预览,请配置 devServer 选项。

    插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。

    控制 Sitemap 链接

    默认情况下,所有除 404 页面以外的网站链接均会被添加进 Sitemap。

    如果你希望在 VuePress 项目页面之外,添加其他页面链接到 Sitemap,请将它们变成数组传入插件的 extraUrls 选项。

    如果你需要排除一些页面路径,你可以将它们变成数组传入到插件的 excludePaths 选项。你也可以在对应页面的 frontmatter 中,设置 sitemapfalse

    输出位置

    你还可以通过插件的 sitemapFilename 选项控制输出的地址,此地址相对于输出目录,默认为 sitemap.xml

    更新周期

    页面默认的更新周期是 daily (每天),如果你希望修改全部的页面周期,请在插件选项中设置 changefreq 。你也可以在页面的 frontmatter 中设置 sitemap.changefreq,页面具有更高的优先级。

    合法的频率有:

    • "always"
    • "hourly"
    • "daily"
    • "weekly"
    • "monthly"
    • "yearly"
    • "never"

    优先级

    你可以在插件中设置 priority 以提供一个默认值。同时你可以通过 frontmatter 中的 sitemap.priority 来为每个页面设置优先级。可接受的值为 01 的浮点数。

    修改时间获取

    你可以通过插件的 modifyTimeGetter 来返回一个 ISO 字符串格式的时间,默认会通过 Git 插件生成。

    以下是一个基于文件最后修改时间的例子。

    // 基于文件最后修改时间
    +({
    +  modifyTimeGetter: (page, app) =>
    +    fs.statSync(app.dir.source(page.filePathRelative)).mtime.toISOString();
    +})
    +

    Sitemap 介绍

    网站地图 (Sitemap) 提供搜索引擎优化 (SEO):

    • 为搜索引擎爬虫提供可以浏览整个网站的链接;
    • 为搜索引擎爬虫提供一些链接,指向动态页面或者采用其他方法比较难以到达的页面;
    • 如果访问者试图访问网站所在域内并不存在的 URL,那么这个访问者就会被转到“无法找到文件”的错误页面,而网站地图可以作为导航页。

    网站地图通过使所有页面可被找到来增强搜索引擎优化的效果。

    大部分搜索引擎只跟踪页面内有限数量的链接,因此当网站非常大的时候,网站地图对于使搜索引擎和访问者可以访问网站中的所有内容就变得必不可少了。

    Sitemaps 是站点管理员向搜索引擎爬虫公布站点可被抓取页面的协议,sitemap 文件内容必须遵循 XML 格式的定义。每个 URL 可以包含更新的周期和时间、URL 在整个站点中的优先级。这样可以让搜索引擎更佳有效的抓取网站内容。

    同步配置 robots.txt

    由于 Sitemap 面向搜索引擎,配合此插件使用时,你最好保证你在 .vuepress/public 文件夹下放置了有效的 robots.txt,以允许搜索引擎收录。一个最简单的 robots.txt 如下 (允许所有搜索引擎访问所有路径)

    User-agent: *
    +
    +Allow: /
    +
    + + + diff --git a/zh/plugins/sitemap/index.html b/zh/plugins/sitemap/index.html new file mode 100644 index 0000000000..f71cbe2a5b --- /dev/null +++ b/zh/plugins/sitemap/index.html @@ -0,0 +1,47 @@ + + + + + + + + + sitemap | VuePress 生态系统 + + + + + +

    sitemap

    @vuepress/plugin-sitemap

    使用

    npm i -D @vuepress/plugin-sitemap@next
    +
    import { sitemapPlugin } from '@vuepress/plugin-sitemap'
    +
    +export default {
    +  plugins: [
    +    sitemapPlugin({
    +      // 选项
    +    }),
    +  ],
    +}
    +
    + + + diff --git a/zh/plugins/theme-data.html b/zh/plugins/theme-data.html new file mode 100644 index 0000000000..325528385e --- /dev/null +++ b/zh/plugins/theme-data.html @@ -0,0 +1,87 @@ + + + + + + + + + theme-data | VuePress 生态系统 + + + + + +

    theme-data

    @vuepress/plugin-theme-data

    为你的主题提供客户端数据,包含 VuePress 的 多语言支持

    该插件主要用于开发主题,并且已经集成到默认主题中。大部分情况下你不需要直接使用它。

    对于主题作者,该插件可以提供与 VuePress 及默认主题相同的多语言支持机制。但是如果你的主题不需要提供多语言支持,或者你想用你自己的方式来实现多语言支持,那么你不需要使用该插件。

    使用方法

    npm i -D @vuepress/plugin-theme-data@next
    +
    import { themeDataPlugin } from '@vuepress/plugin-theme-data'
    +
    +export default {
    +  plugins: [
    +    themeDataPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    配置项

    themeData

    • 类型: ThemeData

    • 默认值: {}

    • 详情:

      你希望在 Client 端中使用的主题数据对象。

      你可以通过该配置项,在 Node 端提供主题数据,然后在客户端通过 useThemeDatauseThemeLocaleData 来使用主题数据。

    • 示例:

    export default {
    +  plugins: [
    +    themeDataPlugin({
    +      themeData: {
    +        foo: 'foo',
    +        locales: {
    +          '/zh/': {
    +            foo: 'zh-foo',
    +          },
    +        },
    +      },
    +    }),
    +  ],
    +}
    +

    注意

    主题数据对象在传递到客户端之前,会使用 JSON.stringify() 进行处理,因此你需要保证你提供的是一个可以被 JSON 序列化的对象。

    Composition API

    useThemeData

    • 详情:

      返回主题数据的 Ref 对象。

      数据是通过 themeData 配置项提供的。

    • 示例:

    import { useThemeData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeData = useThemeData<MyThemeData>()
    +    console.log(themeData.value)
    +  },
    +}
    +

    useThemeLocaleData

    • 详情:

      返回当前 locale 下主题数据的 Ref 对象。

      当前 locale 中的字段已被合并到顶层字段中。

    • 示例:

    import { useThemeLocaleData } from '@vuepress/plugin-theme-data/client'
    +import type { ThemeData } from '@vuepress/plugin-theme-data/client'
    +
    +type MyThemeData = ThemeData<{
    +  foo: string
    +}>
    +
    +export default {
    +  setup() {
    +    const themeLocaleData = useThemeLocaleData<MyThemeData>()
    +    console.log(themeLocaleData.value)
    +  },
    +}
    +
    + + + diff --git a/zh/plugins/toc.html b/zh/plugins/toc.html new file mode 100644 index 0000000000..20201befe8 --- /dev/null +++ b/zh/plugins/toc.html @@ -0,0 +1,110 @@ + + + + + + + + + toc | VuePress 生态系统 + + + + + +

    toc

    @vuepress/plugin-toc

    该插件会提供一个目录 (table-of-contents, TOC) 组件。

    使用方法

    npm i -D @vuepress/plugin-toc@next
    +
    import { tocPlugin } from '@vuepress/plugin-toc'
    +
    +export default {
    +  plugins: [
    +    tocPlugin({
    +      // 配置项
    +    }),
    +  ],
    +}
    +

    与 Markdown 目录语法的区别

    Markdown 目录语法 类似,该插件提供的目录组件可以直接在你的 Markdown 内容中使用:

    <!-- Markdown 目录语法 -->
    +
    +[[toc]]
    +
    +<!-- Vue 目录组件 -->
    +<Toc />
    +

    在 Build 模式中,它们都可以被正确地预渲染。然而,它们之间存在一些区别。

    Markdown 语法 [[toc]] 仅能在 Markdown 文件中使用。它是由 markdown-it 解析的,生成的目录是静态内容。

    组件 <Toc/> 既可以用在 Markdown 文件中,也可以用在 Vue 文件中。它是由 Vue 加载的,生成的目录是一个 Vue 组件。

    该插件可以和 @vuepress/plugin-active-header-links 协同工作,你只需要将 headerLinkSelector 与该插件的 linkClass 匹配即可。当页面滚动至某个标题锚点后,对应的链接就会被加上 linkActiveClass 类名。

    因此,该插件对于主题开发者来说更为有用。

    配置项

    componentName

    • 类型: string

    • 默认值: 'Toc'

    • 详情:

      指定目录组件的名称。

    defaultPropsOptions

    • 类型: Partial<TocPropsOptions>

    • 默认值: {}

    • 详情:

      覆盖组件 options Prop 的默认值。

    组件 Props

    目录组件可以通过 Props 来进行自定义。

    <template>
    +  <Toc :headers="headers" :options="options" />
    +</template>
    +

    headers

    • 类型: PageHeader[]
    interface PageHeader {
    +  level: number
    +  title: string
    +  slug: string
    +  children: PageHeader[]
    +}
    +
    • 详情:

      指定要渲染的标题数组。

      如果该 Prop 没有被设置,默认会使用当前页面的标题。

    options

    • 类型: Partial<TocPropsOptions>
    interface TocPropsOptions {
    +  containerTag: string
    +  containerClass: string
    +  listClass: string
    +  itemClass: string
    +  linkTag: 'a' | 'RouterLink'
    +  linkClass: string
    +  linkActiveClass: string
    +  linkChildrenActiveClass: string
    +}
    +
    const defaultOptions = {
    +  containerTag: 'nav',
    +  containerClass: 'vuepress-toc',
    +  listClass: 'vuepress-toc-list',
    +  itemClass: 'vuepress-toc-item',
    +  linkTag: 'RouterLink',
    +  linkClass: 'vuepress-toc-link',
    +  linkActiveClass: 'active',
    +  linkChildrenActiveClass: 'active',
    +}
    +
    • 详情:

      自定义目录组件。

      如果 containerTag 设置为空字符串 '' ,那么最外层的 <nav> Container 会被完全移除。

    • 示例:

      使用默认 options 的目录组件的渲染结果类似以下结构:

    <template>
    +  <!-- container -->
    +  <nav class="vuepress-toc">
    +    <!-- list -->
    +    <ul class="vuepress-toc-list">
    +      <!-- item -->
    +      <li class="vuepress-toc-item">
    +        <!-- link -->
    +        <RouterLink class="vuepress-toc-link" to="#foo">Foo</RouterLink>
    +      </li>
    +      <!-- item with children -->
    +      <li class="vuepress-toc-item">
    +        <!-- link (children active) -->
    +        <RouterLink class="vuepress-toc-link active" to="#bar">Bar</RouterLink>
    +        <!-- list (children) -->
    +        <ul class="vuepress-toc-list">
    +          <!-- item -->
    +          <li class="vuepress-toc-item">
    +            <!-- link (active) -->
    +            <RouterLink class="vuepress-toc-link active" to="#bar-child">
    +              Bar Child
    +            </RouterLink>
    +          </li>
    +        </ul>
    +      </li>
    +    </ul>
    +  </nav>
    +</template>
    +
    + + + diff --git a/zh/themes/default/components.html b/zh/themes/default/components.html new file mode 100644 index 0000000000..0436698864 --- /dev/null +++ b/zh/themes/default/components.html @@ -0,0 +1,68 @@ + + + + + + + + + 内置组件 | VuePress 生态系统 + + + + + +

    内置组件

    @vuepress/theme-default

    Badge badge

    • Props:

      • type
        • 类型: 'tip' | 'warning' | 'danger'
        • 默认值: 'tip'
      • text
        • 类型: string
        • 默认值: ''
      • vertical
        • 类型: 'top' | 'middle' | 'bottom' | undefined
        • 默认值: undefined
    • 示例:

    输入

    - VuePress - <Badge type="tip" text="v2" vertical="top" />
    +- VuePress - <Badge type="warning" text="v2" vertical="middle" />
    +- VuePress - <Badge type="danger" text="v2" vertical="bottom" />
    +

    输出

    • VuePress - v2
    • VuePress - v2
    • VuePress - v2

    CodeGroup

    CodeGroupItem

    • Props:

      • title
        • 类型: string
        • 是否必需: true
      • active
        • 类型: boolean
        • 默认值: false
    • 详情:

      该组件必须放置在 CodeGroup 组件的内部。

      可以通过 active Prop 来设置初始激活的元素。如果不设置,默认激活第一个元素。

    • 示例:

    输入

    <CodeGroup>
    +  <CodeGroupItem title="pnpm">
    +
    +```bash:no-line-numbers
    +pnpm install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="yarn">
    +
    +```bash:no-line-numbers
    +yarn install
    +```
    +
    +  </CodeGroupItem>
    +
    +  <CodeGroupItem title="npm" active>
    +
    +```bash:no-line-numbers
    +npm install
    +```
    +
    +  </CodeGroupItem>
    +</CodeGroup>
    +

    输出

    pnpm install
    +
    yarn install
    +
    npm install
    +

    注意

    你必须在 <CodeGroupItem> 的开始标签和代码块之间添加一个空行,否则代码块无法被 Markdown 正确解析。

    所有内容首先都必须是合法的 Markdown ,然后才是一个 Vue SFC 。

    了解更多: Cookbook > Markdown 与 Vue SFC在新窗口打开

    或者你可以选择使用 自定义容器

    + + + diff --git a/zh/themes/default/config.html b/zh/themes/default/config.html new file mode 100644 index 0000000000..c2ac25b2e6 --- /dev/null +++ b/zh/themes/default/config.html @@ -0,0 +1,177 @@ + + + + + + + + + 配置 | VuePress 生态系统 + + + + + +

    配置

    基础配置

    hostname

    • 类型: string

    • 详情:

      部署的域名,例如 https://example.com

    locales

    Locale 配置

    该章节内的配置项可以作为一般配置使用,也可以使用在 locales 内。

    colorMode

    colorModeSwitch

    home

    • 类型: string

    • 默认值: /

    • 详情:

      首页的路径。

      它将被用于:

      • 导航栏中 Logo 的链接
      • 404 页面的 返回首页 链接
    • 类型: false | (NavbarItem | NavbarGroup | string)[]

    • 默认值: []

    • 详情:

      导航栏配置。

      设置为 false 可以禁用导航栏。

      为了配置导航栏元素,你可以将其设置为 导航栏数组 ,其中的每个元素是 NavbarItem 对象、 NavbarGroup 对象、或者字符串:

      • NavbarItem 对象应该有一个 text 字段和一个 link 字段,还有一个可选的 activeMatch 字段。
      • NavbarGroup 对象应该有一个 text 字段和一个 children 字段。 children 字段同样是一个 导航栏数组
      • 字符串应为目标页面文件的路径。它将会被转换为 NavbarItem 对象,将页面标题作为 text ,将页面路由路径作为 link
    • 示例 1:

    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // NavbarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +      },
    +      // NavbarGroup
    +      {
    +        text: 'Group',
    +        children: ['/group/foo.md', '/group/bar.md'],
    +      },
    +      // 字符串 - 页面文件路径
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • 示例 2:
    export default {
    +  theme: defaultTheme({
    +    navbar: [
    +      // 嵌套 Group - 最大深度为 2
    +      {
    +        text: 'Group',
    +        children: [
    +          {
    +            text: 'SubGroup',
    +            children: ['/group/sub/foo.md', '/group/sub/bar.md'],
    +          },
    +        ],
    +      },
    +      // 控制元素何时被激活
    +      {
    +        text: 'Group 2',
    +        children: [
    +          {
    +            text: 'Always active',
    +            link: '/',
    +            // 该元素将一直处于激活状态
    +            activeMatch: '/',
    +          },
    +          {
    +            text: 'Active on /foo/',
    +            link: '/not-foo/',
    +            // 该元素在当前路由路径是 /foo/ 开头时激活
    +            // 支持正则表达式
    +            activeMatch: '^/foo/',
    +          },
    +        ],
    +      },
    +    ],
    +  }),
    +}
    +
    • 类型: null | string

    • 详情:

      Logo 图片的 URL。

      Logo 图片将会显示在导航栏的左端。

      设置为 null 可以禁用 Logo 。

    • 示例:

    export default {
    +  theme: defaultTheme({
    +    // Public 文件路径
    +    logo: '/images/hero.png',
    +    // URL
    +    logo: 'https://vuejs.org/images/logo.png',
    +  }),
    +}
    +

    logoDark

    • 类型: null | string

    • 详情:

      在夜间模式中使用的 Logo 图片的 URL。

      如果你想在夜间模式中使用不同的 Logo 图片,就可以使用该配置项。

      设置为 null 可以在夜间模式下禁用 Logo 。忽略该配置项将会在夜间模式中使用 logo 配置。

    • 参考:

    logoAlt

    • 类型:null | string

    • 详情:

      指定 Logo 图片的替代文字。

      当未指定时,将默认与站点标题相同。

    repo

    • 类型: string

    • 详情:

      项目仓库的 URL。

      它将被用作 仓库链接 的链接。仓库链接 将会显示为导航栏的最后一个元素。

    export default {
    +  theme: defaultTheme({
    +    // 如果你按照 `organization/repository` 的格式设置它
    +    // 我们会将它作为一个 GitHub 仓库
    +    repo: 'vuejs/vuepress',
    +    // 你也可以直接将它设置为一个 URL
    +    repo: 'https://gitlab.com/foo/bar',
    +  }),
    +}
    +
    • 类型: false | 'auto' | SidebarConfigArray | SidebarConfigObject

    • 默认值: 'auto'

    • 详情:

      侧边栏配置。

      你可以通过页面的 sidebar frontmatter 来覆盖这个全局配置。

      设置为 false 可以禁用侧边栏。

      如果你设置为 'auto',侧边栏会根据页面标题自动生成。

      为了手动配置侧边栏元素,你可以将其设置为 侧边栏数组 ,其中的每个元素是一个 SidebarItem 对象或者一个字符串:

      • SidebarItem 对象应该有一个 text 字段,有一个可选的 link 字段、一个可选的 children 字段和一个可选的 collapsible 字段。 children 字段同样是一个 侧边栏数组collapsible 字段来控制它是否可折叠。
      • 字符串应为目标页面文件的路径。它将会被转换为 SidebarItem 对象,将页面标题作为 text ,将页面路由路径作为 link ,并根据页面小标题自动生成 children

      如果你想在不同子路径中使用不同的侧边栏,你可以将该配置项设置为 侧边栏对象

      • Key 为路径前缀。
      • Value 为 侧边栏数组"heading" 以自动为相应路径生成基于标题的侧边栏。
    • 示例 1:

    export default {
    +  theme: defaultTheme({
    +    // 侧边栏数组
    +    // 所有页面会使用相同的侧边栏
    +    sidebar: [
    +      // SidebarItem
    +      {
    +        text: 'Foo',
    +        link: '/foo/',
    +        children: [
    +          // SidebarItem
    +          {
    +            text: 'github',
    +            link: 'https://github.com',
    +            children: [],
    +          },
    +          // 字符串 - 页面文件路径
    +          '/foo/bar.md',
    +        ],
    +      },
    +      // 字符串 - 页面文件路径
    +      '/bar/README.md',
    +    ],
    +  }),
    +}
    +
    • 示例 2:
    export default {
    +  theme: defaultTheme({
    +    // 侧边栏对象
    +    // 不同子路径下的页面会使用不同的侧边栏
    +    sidebar: {
    +      '/guide/': [
    +        {
    +          text: 'Guide',
    +          children: ['/guide/introduction.md', '/guide/getting-started.md'],
    +        },
    +      ],
    +      '/reference/': 'heading',
    +    },
    +  }),
    +}
    +
    • 示例 3:
    export default {
    +  theme: defaultTheme({
    +    // 可折叠的侧边栏
    +    sidebar: {
    +      '/reference/': [
    +        {
    +          text: 'VuePress Reference',
    +          collapsible: true,
    +          children: ['/reference/cli.md', '/reference/config.md'],
    +        },
    +        {
    +          text: 'Bundlers Reference',
    +          collapsible: true,
    +          children: [
    +            '/reference/bundler/vite.md',
    +            '/reference/bundler/webpack.md',
    +          ],
    +        },
    +      ],
    +    },
    +  }),
    +}
    +

    sidebarDepth

    • 类型: number

    • 默认值: 2

    • 详情:

      设置根据页面标题自动生成的侧边栏的最大深度。

      • 设为 0 来禁用所有级别的页面标题。
      • 设为 1 来包含 <h2> 标题。
      • 设为 2 来包含 <h2><h3> 标题。
      • ...

      最大值取决于你通过 markdown.headers.level在新窗口打开 提取了哪些级别的标题。

      由于 markdown.headers.level 的默认值是 [2, 3] ,因此 sidebarDepth 的默认最大值是 2

      你可以通过页面的 sidebarDepth frontmatter 来覆盖这个全局配置。

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否启用 编辑此页 链接。

      你可以通过页面的 editLink frontmatter 来覆盖这个全局配置。

    editLinkPattern

    • 类型: string

    • 详情:

      编辑此页 链接的 Pattern 。

      它将会用于生成 编辑此页 的链接。

      如果你不设置该选项,则会根据 docsRepo 配置项来推断 Pattern 。但是如果你的文档仓库没有托管在常用的平台上,比如 GitHub 、 GitLab 、 Bitbucket 、 Gitee 等,那么你必须设置该选项才能使 编辑此页 链接正常工作。

    • 用法:

      Pattern描述
      :repo文档仓库 URL ,即 docsRepo
      :branch文档仓库分支 ,即 docsBranch
      :path页面源文件的路径,即 docsDir 拼接上页面文件的相对路径
    • 示例:

    export default {
    +  theme: defaultTheme({
    +    docsRepo: 'https://gitlab.com/owner/name',
    +    docsBranch: 'master',
    +    docsDir: 'docs',
    +    editLinkPattern: ':repo/-/edit/:branch/:path',
    +  }),
    +}
    +

    则会生成类似于 'https://gitlab.com/owner/name/-/edit/master/docs/path/to/file.md' 的链接。

    docsRepo

    • 类型: string

    • 详情:

      文档源文件的仓库 URL 。

      它将会用于生成 编辑此页 的链接。

      如果你不设置该选项,则默认会使用 repo 配置项。但是如果你的文档源文件是在一个不同的仓库内,你就需要设置该配置项了。

    docsBranch

    • 类型: string

    • 默认值: 'main'

    • 详情:

      文档源文件的仓库分支。

      它将会用于生成 编辑此页 的链接。

    docsDir

    • 类型: string

    • 默认值: ''

    • 详情:

      文档源文件存放在仓库中的目录名。

      它将会用于生成 编辑此页 的链接。

    lastUpdated

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否启用 最近更新时间戳

      你可以通过页面的 lastUpdated frontmatter 来覆盖这个全局配置。要注意的是,如果你已经将该选项设为了 false ,那么这个功能会被完全禁用,并且无法在 locales 或页面 frontmatter 中启用。

    contributors

    • 类型: boolean

    • 默认值: true

    • 详情:

      是否启用 贡献者列表

      你可以通过页面的 contributors frontmatter 来覆盖这个全局配置。要注意的是,如果你已经将该选项设为了 false ,那么这个功能会被完全禁用,并且无法在 locales 或页面 frontmatter 中启用。

    + + + diff --git a/zh/themes/default/extending.html b/zh/themes/default/extending.html new file mode 100644 index 0000000000..381a3475a9 --- /dev/null +++ b/zh/themes/default/extending.html @@ -0,0 +1,101 @@ + + + + + + + + + 继承 | VuePress 生态系统 + + + + + +

    继承

    VuePress 默认主题有着大量的用户,因此我们对它进行了一些便于继承的设计,以便用户轻松进行定制化。

    布局插槽

    默认主题的 Layout 布局提供了一些插槽:

    • navbar
    • navbar-before
    • navbar-after
    • sidebar
    • sidebar-top
    • sidebar-bottom
    • page
    • page-top
    • page-bottom
    • page-content-top
    • page-content-bottom

    在它们的帮助下,你可以很容易地添加或替换内容。下面通过一个示例来介绍一下如何使用布局插槽来继承默认主题。

    首先,创建一个客户端配置文件 .vuepress/client.ts

    import { defineClientConfig } from 'vuepress/client'
    +import Layout from './layouts/Layout.vue'
    +
    +export default defineClientConfig({
    +  layouts: {
    +    Layout,
    +  },
    +})
    +

    接下来,创建 .vuepress/layouts/Layout.vue ,并使用由默认主题的 Layout 布局提供的插槽:

    <script setup>
    +import ParentLayout from '@vuepress/theme-default/layouts/Layout.vue'
    +</script>
    +
    +<template>
    +  <ParentLayout>
    +    <template #page-bottom>
    +      <div class="my-footer">This is my custom page footer</div>
    +    </template>
    +  </ParentLayout>
    +</template>
    +
    +<style lang="css">
    +.my-footer {
    +  text-align: center;
    +}
    +</style>
    +

    此时默认的 Layout 布局已经被你的本地布局覆盖,将会在除了首页外的所有页面添加一个自定义的页脚:

    extending-a-theme

    组件替换

    布局插槽十分实用,但有时候你可能会觉得它不够灵活。默认主题同样提供了替换单个组件的能力。

    默认主题将所有 非全局的组件在新窗口打开 都注册了一个带 @theme 前缀的 alias在新窗口打开 。例如,HomeFooter.vue 的别名是 @theme/HomeFooter.vue

    接下来,如果你想要替换 HomeFooter.vue 组件,只需要在配置文件 .vuepress/config.ts 中覆盖这个别名即可:

    import { defaultTheme } from '@vuepress/theme-default'
    +import { getDirname, path } from 'vuepress/utils'
    +import { defineUserConfig } from 'vuepress'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export default defineUserConfig({
    +  theme: defaultTheme(),
    +  alias: {
    +    '@theme/HomeFooter.vue': path.resolve(
    +      __dirname,
    +      './components/MyHomeFooter.vue',
    +    ),
    +  },
    +})
    +

    开发一个子主题

    除了在 .vuepress/config.ts.vuepress/client.ts 中直接扩展默认主题以外,你可以通过继承默认主题来开发一个你自己的主题:

    import { defaultTheme, type DefaultThemeOptions } from '@vuepress/theme-default'
    +import type { Theme } from 'vuepress/core'
    +import { getDirname, path } from 'vuepress/utils'
    +
    +const __dirname = getDirname(import.meta.url)
    +
    +export const childTheme = (options: DefaultThemeOptions): Theme => {
    +  return {
    +    name: 'vuepress-theme-child',
    +    extends: defaultTheme(options),
    +
    +    // 在子主题的客户端配置文件中覆盖布局
    +    // 注意,你在发布到 NPM 之前会将 TS 构建为 JS ,因此这里需要设置为 JS 文件的路径
    +    clientConfigFile: path.resolve(__dirname, './client.js'),
    +
    +    // 覆盖组件别名
    +    alias: {
    +      '@theme/HomeFooter.vue': path.resolve(
    +        __dirname,
    +        './components/MyHomeFooter.vue',
    +      ),
    +    },
    +  }
    +}
    +
    + + + diff --git a/zh/themes/default/frontmatter.html b/zh/themes/default/frontmatter.html new file mode 100644 index 0000000000..233867c057 --- /dev/null +++ b/zh/themes/default/frontmatter.html @@ -0,0 +1,96 @@ + + + + + + + + + Frontmatter | VuePress 生态系统 + + + + + +

    Frontmatter

    @vuepress/theme-default

    所有页面

    本章节中的 Frontmatter 会在所有类型的页面中生效。

    externalLinkIcon

    • 类型: boolean

    • 详情:

      是否在当前页面展示导航栏。

      如果你在主题配置中禁用了导航栏,那么该 Frontmatter 将不会生效。

    • 参考:

    pageClass

    • 类型: string

    • 详情:

      为当前页面添加额外的类名。

    • 示例:

    ---
    +pageClass: custom-page-class
    +---
    +

    然后你可以在 .vuepress/styles/index.scss 文件中为这个页面添加自定义样式:

    .theme-container.custom-page-class {
    +  /* 页面样式 */
    +}
    +

    首页

    本章节中的 Frontmatter 只会在首页中生效。

    home

    • 类型: boolean

    • 详情:

      设定该页面是首页还是普通页面。

      如果你不设置该 Frontmatter 或将其设为 false ,则该页面会是一个 普通页面

    • 示例:

      ---
      +home: true
      +---
      +

    heroImage

    heroImageDark

    heroAlt

    • 类型: string

    • 详情:

      首页图片的 alt 属性。

      如果不设置,则默认使用 heroText

    heroHeight

    • 类型: number

    • 默认值: 280

    • 详情:

      首页图片 <img> 标签的 height 属性。

      当你的首页图片高度小于默认值时,你可能需要减小该属性。

      需要注意的是,首页图片的高度同样受到了 CSS 的约束。设置这个属性主要是为了减少由加载首页图片引起的 累积布局偏移 (CLS)在新窗口打开

    heroText

    • 类型: string | null

    • 详情:

      首页的大标题。

      如果不设置,则默认使用站点 title在新窗口打开

      设置为 null 来禁用首页大标题。

    tagline

    • 类型: string | null

    • 详情:

      首页的标语。

      如果不设置,则默认使用站点 description在新窗口打开

      设置为 null 来禁用首页标语。

    actions

    • 类型:
    Array<{
    +  text: string
    +  link: string
    +  type?: 'primary' | 'secondary'
    +}>
    +
    • 详情:

      配置首页按钮。

    • 示例:

    ---
    +actions:
    +  - text: 快速上手
    +    link: /zh/guide/getting-started.html
    +    type: primary
    +  - text: 项目简介
    +    link: /zh/guide/introduction.html
    +    type: secondary
    +---
    +

    features

    • 类型:
    Array<{
    +  title: string
    +  details: string
    +}>
    +
    • 详情:

      配置首页特性列表。

    • 示例:

    ---
    +features:
    +  - title: 简洁至上
    +    details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
    +  - title: Vue 驱动
    +    details: 享受 Vue 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
    +  - title: 高性能
    +    details: VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。
    +---
    +
    • 类型: string

    • 详情:

      首页的页脚。

    footerHtml

    • 类型: boolean

    • 详情:

      是否允许页脚中使用 HTML 。

      如果设置为 true ,那么 footer 会被作为 HTML 代码处理。

    普通页面

    本章节中的 Frontmatter 只会在普通页面中生效。

    editLinkPattern

    lastUpdated

    contributors

    sidebarDepth

    prev

    • 类型: NavLink | string

    • 详情:

      上一个页面的链接。

      如果你不设置该 Frontmatter ,该链接会自动根据侧边栏配置进行推断。

      为了手动配置上一页面的链接,你可以将其设置为一个 NavLink 对象或者一个字符串:

      • NavLink 对象应该有一个 text 字段和一个 link 字段。
      • 字符串应为目标页面文件的路径。它将会被转换为 NavLink 对象,将页面标题作为 text ,将页面路由路径作为 link
    • 示例:

    ---
    +# NavLink
    +prev:
    +  text: Get Started
    +  link: /guide/getting-started.html
    +
    +# NavLink - 外部 URL
    +prev:
    +  text: GitHub
    +  link: https://github.com
    +
    +# 字符串 - 页面文件路径
    +prev: /guide/getting-started.md
    +
    +# 字符串 - 页面文件相对路径
    +prev: ../../guide/getting-started.md
    +---
    +

    next

    • 类型: NavLink | string

    • 详情:

      下一个页面的链接。

      如果你不设置该 Frontmatter ,该链接会自动根据侧边栏配置进行推断。

      类型和 prev Frontmatter 相同。

    + + + diff --git a/zh/themes/default/index.html b/zh/themes/default/index.html new file mode 100644 index 0000000000..e27863e8e6 --- /dev/null +++ b/zh/themes/default/index.html @@ -0,0 +1,45 @@ + + + + + + + + + 使用方法 | VuePress 生态系统 + + + + + + + + + diff --git a/zh/themes/default/locale.html b/zh/themes/default/locale.html new file mode 100644 index 0000000000..59651d392b --- /dev/null +++ b/zh/themes/default/locale.html @@ -0,0 +1,57 @@ + + + + + + + + + 语言配置 | VuePress 生态系统 + + + + + +

    语言配置

    这些选项用于配置与语言相关的文本。

    如果你的站点是以英语以外的其他语言提供服务的,你应该为每个语言设置这些选项来提供翻译。

    repoLabel

    • 类型: string

    • 详情:

      项目仓库的标签。

      它将被用作 仓库链接 的文字。仓库链接 将会显示为导航栏的最后一个元素。

      如果你不明确指定该配置项,它将会根据 repo 配置项自动推断。

    selectLanguageText

    • 类型: string

    • 详情:

      选择语言菜单 的文字。

      如果你在站点配置中设置了多个 locales在新窗口打开 ,那么 选择语言菜单 就会显示在导航栏中仓库按钮的旁边。

    selectLanguageAriaLabel

    • 类型: string

    • 详情:

      选择语言菜单aria-label 属性。

      它主要是为了站点的可访问性 (a11y) 。

    selectLanguageName

    • 类型: string

    • 详情:

      Locale 的语言名称。

      该配置项 仅能在主题配置的 locales 的内部生效 。它将被用作 locale 的语言名称,展示在 选择语言菜单 内。

    • 示例:

    export default {
    +  locales: {
    +    '/': {
    +      lang: 'en-US',
    +    },
    +    '/zh/': {
    +      lang: 'zh-CN',
    +    },
    +  },
    +  theme: defaultTheme({
    +    locales: {
    +      '/': {
    +        selectLanguageName: 'English',
    +      },
    +      '/zh/': {
    +        selectLanguageName: '简体中文',
    +      },
    +    },
    +  }),
    +}
    +
    • 类型:null | string

    • 详情:

      导航栏中主导航 aria-label 属性的值。

    • 类型:null | string

    • 详情:

      下一页/上一页导航 aria-label 属性的值

    editLinkText

    • 类型: string

    • 默认值: 'Edit this page'

    • 详情:

      编辑此页 链接的文字。

    lastUpdatedText

    • 类型: string

    • 默认值: 'Last Updated'

    • 详情:

      最近更新时间戳 标签的文字。

    contributorsText

    • 类型: string

    • 默认值: 'Contributors'

    • 详情:

      贡献者列表 标签的文字。

    tip

    • 类型: string

    • 默认值: 'TIP'

    • 详情:

      Tip 自定义容器 的默认标题。

    warning

    • 类型: string

    • 默认值: 'WARNING'

    • 详情:

      Warning 自定义容器 的默认标题。

    danger

    • 类型: string

    • 默认值: 'DANGER'

    • 详情:

      Danger 自定义容器 的默认标题。

    notFound

    • 类型: string[]

    • 默认值: ['Not Found']

    • 详情:

      404 页面的提示信息。

      当用户进入 404 页面时,会从数组中随机选取一条信息进行展示。

    backToHome

    • 类型: string

    • 默认值: 'Back to home'

    • 详情:

      404 页面中 返回首页 链接的文字。

    openInNewWindow

    toggleColorMode

    • 类型: string

    • 默认值: 'toggle color mode'

    • 详情:

      切换颜色模式按钮的标题文字。

      它主要是为了站点的可访问性 (a11y) 。

    • 参考:

    toggleSidebar

    • 类型: string

    • 默认值: 'toggle sidebar'

    • 详情:

      切换侧边栏按钮的标题文字。

      它主要是为了站点的可访问性 (a11y) 。

    + + + diff --git a/zh/themes/default/markdown.html b/zh/themes/default/markdown.html new file mode 100644 index 0000000000..310995ae25 --- /dev/null +++ b/zh/themes/default/markdown.html @@ -0,0 +1,86 @@ + + + + + + + + + Markdown | VuePress 生态系统 + + + + + +

    Markdown

    @vuepress/theme-default

    自定义容器

    • 使用:

      ::: <type> [title]
      +[content]
      +:::
      +

      type 是必需的, titlecontent 是可选的。

      支持的 type 有:

    • 示例 1 (默认标题):

    输入

    ::: tip
    +这是一个提示
    +:::
    +
    +::: warning
    +这是一个警告
    +:::
    +
    +::: danger
    +这是一个危险警告
    +:::
    +
    +::: details
    +这是一个 details 标签
    +:::
    +

    输出

    提示

    这是一个提示

    注意

    这是一个警告

    警告

    这是一个危险警告

    这是一个 details 标签

    • 示例 2 (自定义标题):

    输入

    ::: danger STOP
    +危险区域,禁止通行
    +:::
    +
    +::: details 点击查看代码
    +
    +```ts
    +console.log('你好,VuePress!')
    +```
    +
    +:::
    +

    输出

    STOP

    危险区域,禁止通行

    点击查看代码
    console.log('你好,VuePress!')
    +
    • 示例 3 (Code Group 别名):

    输入

    :::: code-group
    +::: code-group-item FOO
    +
    +```ts
    +const foo = 'foo'
    +```
    +
    +:::
    +
    +::: code-group-item BAR
    +
    +```ts
    +const bar = 'bar'
    +```
    +
    +:::
    +::::
    +

    输出

    const foo = 'foo'
    +
    const bar = 'bar'
    +
    + + + diff --git a/zh/themes/default/plugin.html b/zh/themes/default/plugin.html new file mode 100644 index 0000000000..9d7d71b98c --- /dev/null +++ b/zh/themes/default/plugin.html @@ -0,0 +1,46 @@ + + + + + + + + + 插件配置 | VuePress 生态系统 + + + + + +

    插件配置

    你可以通过 themePlugins 设置默认主题使用的插件。

    默认主题使用了一些插件,如果你确实不需要该插件,你可以选择禁用它。在禁用插件之前,请确保你已了解它的用途。

    import { defaultTheme } from '@vuepress/theme-default'
    +
    +export default {
    +  theme: defaultTheme({
    +    themePlugins: {
    +      // 在这里自定义主题插件
    +    },
    +  }),
    +}
    +

    themePlugins.backToTop

    • 类型: BackToTopPluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-back-to-top

      支持对象格式以作为插件选项。

    themePlugins.container

    themePlugins.copyCode

    • 类型: CopyCodePluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-copy-code

      支持对象格式以作为插件选项。

    themePlugins.externalLinkIcon

    themePlugins.git

    themePlugins.mediumZoom

    themePlugins.nprogress

    themePlugins.seo

    • 类型: SeoPluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-seo

      支持对象格式以作为插件选项。

    themePlugins.sitemap

    • 类型: SitemapPluginOptions | boolean

    • 默认值: true

    • 详情:

      是否启用 @vuepress/plugin-sitemap

      支持对象格式以作为插件选项。

    + + + diff --git a/zh/themes/default/styles.html b/zh/themes/default/styles.html new file mode 100644 index 0000000000..b37a8b00c0 --- /dev/null +++ b/zh/themes/default/styles.html @@ -0,0 +1,268 @@ + + + + + + + + + 样式 | VuePress 生态系统 + + + + + +

    样式

    @vuepress/theme-default

    默认主题使用 SASS在新窗口打开 作为 CSS 预处理器。

    用户可以通过 palette 文件 来自定义样式变量,还可以通过 style 文件 来添加额外的样式。

    Palette 文件

    Palette 文件的路径是 .vuepress/styles/palette.scss

    你可以利用它来覆盖默认主题的预定义 SASS 变量。

    点击查看 SASS 变量
    // responsive breakpoints
    +$MQNarrow: 959px !default;
    +$MQMobile: 719px !default;
    +$MQMobileNarrow: 419px !default;
    +

    Style 文件

    Style 文件的路径是 .vuepress/styles/index.scss

    你可以在这里添加额外的样式,或者覆盖默认样式:

    :root {
    +  scroll-behavior: smooth;
    +}
    +

    你也可以利用它来覆盖默认主题的预定义 CSS 变量。

    点击查看 CSS 变量
    :root {
    +  // brand colors
    +  --c-brand: #3eaf7c;
    +  --c-brand-light: #4abf8a;
    +
    +  // background colors
    +  --c-bg: #ffffff;
    +  --c-bg-light: #f3f4f5;
    +  --c-bg-lighter: #eeeeee;
    +  --c-bg-dark: #ebebec;
    +  --c-bg-darker: #e6e6e6;
    +  --c-bg-navbar: var(--c-bg);
    +  --c-bg-sidebar: var(--c-bg);
    +  --c-bg-arrow: #cccccc;
    +
    +  // text colors
    +  --c-text: #2c3e50;
    +  --c-text-accent: var(--c-brand);
    +  --c-text-light: #3a5169;
    +  --c-text-lighter: #4e6e8e;
    +  --c-text-lightest: #6a8bad;
    +  --c-text-quote: #999999;
    +
    +  // border colors
    +  --c-border: #eaecef;
    +  --c-border-dark: #dfe2e5;
    +
    +  // custom container colors
    +  --c-tip: #42b983;
    +  --c-tip-bg: var(--c-bg-light);
    +  --c-tip-title: var(--c-text);
    +  --c-tip-text: var(--c-text);
    +  --c-tip-text-accent: var(--c-text-accent);
    +  --c-warning: #ffc310;
    +  --c-warning-bg: #fffae3;
    +  --c-warning-bg-light: #fff3ba;
    +  --c-warning-bg-lighter: #fff0b0;
    +  --c-warning-border-dark: #f7dc91;
    +  --c-warning-details-bg: #fff5ca;
    +  --c-warning-title: #f1b300;
    +  --c-warning-text: #746000;
    +  --c-warning-text-accent: #edb100;
    +  --c-warning-text-light: #c1971c;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #f11e37;
    +  --c-danger-bg: #ffe0e0;
    +  --c-danger-bg-light: #ffcfde;
    +  --c-danger-bg-lighter: #ffc9c9;
    +  --c-danger-border-dark: #f1abab;
    +  --c-danger-details-bg: #ffd4d4;
    +  --c-danger-title: #ed1e2c;
    +  --c-danger-text: #660000;
    +  --c-danger-text-accent: #bd1a1a;
    +  --c-danger-text-light: #b5474d;
    +  --c-danger-text-quote: #c15b5b;
    +  --c-details-bg: #eeeeee;
    +
    +  // badge component colors
    +  --c-badge-tip: var(--c-tip);
    +  --c-badge-warning: #ecc808;
    +  --c-badge-warning-text: var(--c-bg);
    +  --c-badge-danger: #dc2626;
    +  --c-badge-danger-text: var(--c-bg);
    +
    +  // code group colors
    +  --c-code-group-tab-title: rgba(255, 255, 255, 0.9);
    +  --c-code-group-tab-bg: var(--code-bg-color);
    +  --c-code-group-tab-outline: var(var(--c-code-group-tab-title));
    +  --c-code-group-tab-active-border: var(--c-brand);
    +
    +  // transition vars
    +  --t-color: 0.3s ease;
    +  --t-transform: 0.3s ease;
    +
    +  // code blocks vars
    +  --code-bg-color: #282c34;
    +  --code-hl-bg-color: rgba(0, 0, 0, 0.66);
    +  --code-ln-color: #9e9e9e;
    +  --code-ln-wrapper-width: 3.5rem;
    +
    +  // font vars
    +  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
    +    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
    +  --font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
    +
    +  // layout vars
    +  --navbar-height: 3.6rem;
    +  --navbar-padding-v: 0.7rem;
    +  --navbar-padding-h: 1.5rem;
    +  --sidebar-width: 20rem;
    +  --sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
    +  --content-width: 740px;
    +  --homepage-width: 960px;
    +}
    +
    +// plugin-back-to-top
    +.back-to-top {
    +  --back-to-top-color: var(--c-brand);
    +  --back-to-top-color-hover: var(--c-brand-light);
    +  --back-to-top-bg-color: var(--c-bg);
    +}
    +
    +// plugin-docsearch
    +.DocSearch {
    +  --docsearch-primary-color: var(--c-brand);
    +  --docsearch-text-color: var(--c-text);
    +  --docsearch-highlight-color: var(--c-brand);
    +  --docsearch-muted-color: var(--c-text-quote);
    +  --docsearch-container-background: rgba(9, 10, 17, 0.8);
    +  --docsearch-modal-background: var(--c-bg-light);
    +  --docsearch-searchbox-background: var(--c-bg-lighter);
    +  --docsearch-searchbox-focus-background: var(--c-bg);
    +  --docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
    +  --docsearch-hit-color: var(--c-text-light);
    +  --docsearch-hit-active-color: var(--c-bg);
    +  --docsearch-hit-background: var(--c-bg);
    +  --docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
    +  --docsearch-footer-background: var(--c-bg);
    +}
    +
    +// plugin-external-link-icon
    +.external-link-icon {
    +  --external-link-icon-color: var(--c-text-quote);
    +}
    +
    +// plugin-medium-zoom
    +.medium-zoom-overlay {
    +  --medium-zoom-bg-color: var(--c-bg);
    +}
    +
    +// plugin-nprogress
    +#nprogress {
    +  --nprogress-color: var(--c-brand);
    +}
    +
    +// plugin-pwa-popup
    +.pwa-popup {
    +  --pwa-popup-text-color: var(--c-text);
    +  --pwa-popup-bg-color: var(--c-bg);
    +  --pwa-popup-border-color: var(--c-brand);
    +  --pwa-popup-shadow: 0 4px 16px var(--c-brand);
    +  --pwa-popup-btn-text-color: var(--c-bg);
    +  --pwa-popup-btn-bg-color: var(--c-brand);
    +  --pwa-popup-btn-hover-bg-color: var(--c-brand-light);
    +}
    +
    +// plugin-search
    +.search-box {
    +  --search-bg-color: var(--c-bg);
    +  --search-accent-color: var(--c-brand);
    +  --search-text-color: var(--c-text);
    +  --search-border-color: var(--c-border);
    +
    +  --search-item-text-color: var(--c-text-lighter);
    +  --search-item-focus-bg-color: var(--c-bg-light);
    +}
    +
    点击查看暗黑模式 CSS 变量
    html.dark {
    +  // brand colors
    +  --c-brand: #3aa675;
    +  --c-brand-light: #349469;
    +
    +  // background colors
    +  --c-bg: #22272e;
    +  --c-bg-light: #2b313a;
    +  --c-bg-lighter: #262c34;
    +  --c-bg-dark: #343b44;
    +  --c-bg-darker: #37404c;
    +
    +  // text colors
    +  --c-text: #adbac7;
    +  --c-text-light: #96a7b7;
    +  --c-text-lighter: #8b9eb0;
    +  --c-text-lightest: #8094a8;
    +
    +  // border colors
    +  --c-border: #3e4c5a;
    +  --c-border-dark: #34404c;
    +
    +  // custom container colors
    +  --c-tip: #318a62;
    +  --c-warning: #e0ad15;
    +  --c-warning-bg: #2d2f2d;
    +  --c-warning-bg-light: #423e2a;
    +  --c-warning-bg-lighter: #44442f;
    +  --c-warning-border-dark: #957c35;
    +  --c-warning-details-bg: #39392d;
    +  --c-warning-title: #fdca31;
    +  --c-warning-text: #d8d96d;
    +  --c-warning-text-accent: #ffbf00;
    +  --c-warning-text-light: #ddb84b;
    +  --c-warning-text-quote: #ccab49;
    +  --c-danger: #fc1e38;
    +  --c-danger-bg: #39232c;
    +  --c-danger-bg-light: #4b2b35;
    +  --c-danger-bg-lighter: #553040;
    +  --c-danger-border-dark: #a25151;
    +  --c-danger-details-bg: #482936;
    +  --c-danger-title: #fc2d3b;
    +  --c-danger-text: #ea9ca0;
    +  --c-danger-text-accent: #fd3636;
    +  --c-danger-text-light: #d9777c;
    +  --c-danger-text-quote: #d56b6b;
    +  --c-details-bg: #323843;
    +
    +  // badge component colors
    +  --c-badge-warning: var(--c-warning);
    +  --c-badge-warning-text: #3c2e05;
    +  --c-badge-danger: var(--c-danger);
    +  --c-badge-danger-text: #401416;
    +
    +  // code blocks vars
    +  --code-hl-bg-color: #363b46;
    +}
    +
    +// plugin-docsearch
    +html.dark .DocSearch {
    +  --docsearch-logo-color: var(--c-text);
    +  --docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
    +  --docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
    +    0 2px 2px 0 rgba(3, 4, 9, 0.3);
    +  --docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
    +  --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
    +    0 -4px 8px 0 rgba(0, 0, 0, 0.2);
    +}
    +
    + + +