diff --git a/404.html b/404.html new file mode 100644 index 00000000..55bef983 --- /dev/null +++ b/404.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + ngseke + +
@ngseke

Hi, I'm Sean. 🍻

現職前端軟體工程師,專注於 TypeScriptVueReact
追求撰寫無瑕程式碼是我的開發格言,並致力於優化開發者體驗。
熱衷探究前端領域的新鮮事,期待藉由知識分享為社群創造影響力。

aka 黃省喬 / HUANG Sing-Ciao

Skills

  • React
  • Vue 3
  • Tailwind CSS
  • Nuxt
  • TypeScript
  • Node.js

Experience

  • Software Engineer @ ASUS AICS
    2022 — 2023
  • Software Engineer @ 3drens
    2021 — 2022
  • Software Engineer @ gogoout
    2019 — 2021
  • Summer Engineering Intern @ Hiero7
    Summer 2018

Contact Me

Projects

  • Versatile Npm

    自訂 Npm 安裝指令瀏覽器擴充功能

  • LeetCode Night

    LeetCode 深色模式瀏覽器擴充功能

Recent Posts

+ + + + \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..63cac4cf --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +ngseke.me diff --git a/about.html b/about.html new file mode 100644 index 00000000..de4a1d67 --- /dev/null +++ b/about.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + ngseke + +
@ngseke

Hi, I'm Sean. 🍻

現職前端軟體工程師,專注於 TypeScriptVueReact
追求撰寫無瑕程式碼是我的開發格言,並致力於優化開發者體驗。
熱衷探究前端領域的新鮮事,期待藉由知識分享為社群創造影響力。

aka 黃省喬 / HUANG Sing-Ciao

Skills

  • React
  • Vue 3
  • Tailwind CSS
  • Nuxt
  • TypeScript
  • Node.js

Experience

  • Software Engineer @ ASUS AICS
    2022 — 2023
  • Software Engineer @ 3drens
    2021 — 2022
  • Software Engineer @ gogoout
    2019 — 2021
  • Summer Engineering Intern @ Hiero7
    Summer 2018

Contact Me

Projects

  • Versatile Npm

    自訂 Npm 安裝指令瀏覽器擴充功能

  • LeetCode Night

    LeetCode 深色模式瀏覽器擴充功能

Recent Posts

+ + + + \ No newline at end of file diff --git a/assets/0-11090461.png b/assets/0-11090461.png new file mode 100644 index 00000000..b239b253 Binary files /dev/null and b/assets/0-11090461.png differ diff --git a/assets/1-408d5807.png b/assets/1-408d5807.png new file mode 100644 index 00000000..5f9f5548 Binary files /dev/null and b/assets/1-408d5807.png differ diff --git a/assets/1-c36f7e53.png b/assets/1-c36f7e53.png new file mode 100644 index 00000000..aa7662b6 Binary files /dev/null and b/assets/1-c36f7e53.png differ diff --git a/assets/1-ce441fa9.png b/assets/1-ce441fa9.png new file mode 100644 index 00000000..18d20a0f Binary files /dev/null and b/assets/1-ce441fa9.png differ diff --git a/assets/1-d295ad8e.png b/assets/1-d295ad8e.png new file mode 100644 index 00000000..8f215dda Binary files /dev/null and b/assets/1-d295ad8e.png differ diff --git a/assets/1-f5f11eb3.png b/assets/1-f5f11eb3.png new file mode 100644 index 00000000..808f21ae Binary files /dev/null and b/assets/1-f5f11eb3.png differ diff --git a/assets/1400x560-02b6cc6e.png b/assets/1400x560-02b6cc6e.png new file mode 100644 index 00000000..a1f468db Binary files /dev/null and b/assets/1400x560-02b6cc6e.png differ diff --git a/assets/2-4f25068f.png b/assets/2-4f25068f.png new file mode 100644 index 00000000..d98c3548 Binary files /dev/null and b/assets/2-4f25068f.png differ diff --git a/assets/2-6e0e5062.png b/assets/2-6e0e5062.png new file mode 100644 index 00000000..a45d1a05 Binary files /dev/null and b/assets/2-6e0e5062.png differ diff --git a/assets/2-8417bfde.png b/assets/2-8417bfde.png new file mode 100644 index 00000000..40293478 Binary files /dev/null and b/assets/2-8417bfde.png differ diff --git a/assets/2-a067597c.png b/assets/2-a067597c.png new file mode 100644 index 00000000..48ae80e9 Binary files /dev/null and b/assets/2-a067597c.png differ diff --git a/assets/2-c1925776.png b/assets/2-c1925776.png new file mode 100644 index 00000000..b831cd0a Binary files /dev/null and b/assets/2-c1925776.png differ diff --git a/assets/3-78ab6d35.png b/assets/3-78ab6d35.png new file mode 100644 index 00000000..e31004cd Binary files /dev/null and b/assets/3-78ab6d35.png differ diff --git a/assets/3-c180c5f6.png b/assets/3-c180c5f6.png new file mode 100644 index 00000000..892aee05 Binary files /dev/null and b/assets/3-c180c5f6.png differ diff --git a/assets/3-ff7def49.png b/assets/3-ff7def49.png new file mode 100644 index 00000000..6936d93a Binary files /dev/null and b/assets/3-ff7def49.png differ diff --git a/assets/4-708e3caa.png b/assets/4-708e3caa.png new file mode 100644 index 00000000..a7d4d98f Binary files /dev/null and b/assets/4-708e3caa.png differ diff --git a/assets/404-c7f247d5.js b/assets/404-c7f247d5.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/404-c7f247d5.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/5-0224294b.png b/assets/5-0224294b.png new file mode 100644 index 00000000..fe2acd55 Binary files /dev/null and b/assets/5-0224294b.png differ diff --git a/assets/6-a159bc1d.png b/assets/6-a159bc1d.png new file mode 100644 index 00000000..af1d65d8 Binary files /dev/null and b/assets/6-a159bc1d.png differ diff --git a/assets/7-cb414675.png b/assets/7-cb414675.png new file mode 100644 index 00000000..c75fd15a Binary files /dev/null and b/assets/7-cb414675.png differ diff --git a/assets/_...404_-c7f247d5.js b/assets/_...404_-c7f247d5.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/_...404_-c7f247d5.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/about-c7f247d5.js b/assets/about-c7f247d5.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/about-c7f247d5.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/after-8a80ee82.jpg b/assets/after-8a80ee82.jpg new file mode 100644 index 00000000..5b04d2ed Binary files /dev/null and b/assets/after-8a80ee82.jpg differ diff --git a/assets/after-97d65a70.png b/assets/after-97d65a70.png new file mode 100644 index 00000000..cca0e136 Binary files /dev/null and b/assets/after-97d65a70.png differ diff --git a/assets/app-033a95d9.js b/assets/app-033a95d9.js new file mode 100644 index 00000000..c58c4f1e --- /dev/null +++ b/assets/app-033a95d9.js @@ -0,0 +1,793 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const o of a.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();function no(e,t){const n=Object.create(null),r=e.split(",");for(let i=0;i!!n[i.toLowerCase()]:i=>!!n[i]}const zf="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Uf=no(zf);function oc(e){return!!e||e===""}function Hn(e){if(oe(e)){const t={};for(let n=0;n{if(n){const r=n.split(Wf);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function yt(e){let t="";if(De(e))t=e;else if(oe(e))for(let n=0;nDe(e)?e:e==null?"":oe(e)||$e(e)&&(e.toString===uc||!fe(e.toString))?JSON.stringify(e,sc,2):String(e),sc=(e,t)=>t&&t.__v_isRef?sc(e,t.value):Sn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,i])=>(n[`${r} =>`]=i,n),{})}:lc(t)?{[`Set(${t.size})`]:[...t.values()]}:$e(t)&&!oe(t)&&!fc(t)?String(t):t,Te={},On=[],ut=()=>{},qf=()=>!1,Gf=/^on[^a-z]/,Ar=e=>Gf.test(e),ro=e=>e.startsWith("onUpdate:"),ze=Object.assign,io=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Qf=Object.prototype.hasOwnProperty,ge=(e,t)=>Qf.call(e,t),oe=Array.isArray,Sn=e=>$i(e)==="[object Map]",lc=e=>$i(e)==="[object Set]",fe=e=>typeof e=="function",De=e=>typeof e=="string",ao=e=>typeof e=="symbol",$e=e=>e!==null&&typeof e=="object",cc=e=>$e(e)&&fe(e.then)&&fe(e.catch),uc=Object.prototype.toString,$i=e=>uc.call(e),Xf=e=>$i(e).slice(8,-1),fc=e=>$i(e)==="[object Object]",oo=e=>De(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,ir=no(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Li=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Jf=/-(\w)/g,wt=Li(e=>e.replace(Jf,(t,n)=>n?n.toUpperCase():"")),Zf=/\B([A-Z])/g,Bn=Li(e=>e.replace(Zf,"-$1").toLowerCase()),Ri=Li(e=>e.charAt(0).toUpperCase()+e.slice(1)),ra=Li(e=>e?`on${Ri(e)}`:""),hr=(e,t)=>!Object.is(e,t),ia=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},dc=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Go;const ed=()=>Go||(Go=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});let Qe;class td{constructor(t=!1){this.active=!0,this.effects=[],this.cleanups=[],!t&&Qe&&(this.parent=Qe,this.index=(Qe.scopes||(Qe.scopes=[])).push(this)-1)}run(t){if(this.active){const n=Qe;try{return Qe=this,t()}finally{Qe=n}}}on(){Qe=this}off(){Qe=this.parent}stop(t){if(this.active){let n,r;for(n=0,r=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},pc=e=>(e.w&Wt)>0,mc=e=>(e.n&Wt)>0,ad=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r{(c==="length"||c>=r)&&s.push(l)});else switch(n!==void 0&&s.push(o.get(n)),t){case"add":oe(e)?oo(n)&&s.push(o.get("length")):(s.push(o.get(mn)),Sn(e)&&s.push(o.get(ka)));break;case"delete":oe(e)||(s.push(o.get(mn)),Sn(e)&&s.push(o.get(ka)));break;case"set":Sn(e)&&s.push(o.get(mn));break}if(s.length===1)s[0]&&xa(s[0]);else{const l=[];for(const c of s)c&&l.push(...c);xa(so(l))}}function xa(e,t){const n=oe(e)?e:[...e];for(const r of n)r.computed&&Xo(r);for(const r of n)r.computed||Xo(r)}function Xo(e,t){(e!==ot||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}const sd=no("__proto__,__v_isRef,__isVue"),vc=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ao)),ld=co(),cd=co(!1,!0),ud=co(!0),Jo=fd();function fd(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=ye(this);for(let a=0,o=this.length;a{e[t]=function(...n){zn();const r=ye(this)[t].apply(this,n);return Un(),r}}),e}function co(e=!1,t=!1){return function(r,i,a){if(i==="__v_isReactive")return!e;if(i==="__v_isReadonly")return e;if(i==="__v_isShallow")return t;if(i==="__v_raw"&&a===(e?t?Sd:kc:t?wc:yc).get(r))return r;const o=oe(r);if(!e&&o&&ge(Jo,i))return Reflect.get(Jo,i,a);const s=Reflect.get(r,i,a);return(ao(i)?vc.has(i):sd(i))||(e||Ze(r,"get",i),t)?s:Be(s)?o&&oo(i)?s:s.value:$e(s)?e?Di(s):Tr(s):s}}const dd=bc(),pd=bc(!0);function bc(e=!1){return function(n,r,i,a){let o=n[r];if($n(o)&&Be(o)&&!Be(i))return!1;if(!e&&(!ui(i)&&!$n(i)&&(o=ye(o),i=ye(i)),!oe(n)&&Be(o)&&!Be(i)))return o.value=i,!0;const s=oe(n)&&oo(r)?Number(r)e,Ni=e=>Reflect.getPrototypeOf(e);function Dr(e,t,n=!1,r=!1){e=e.__v_raw;const i=ye(e),a=ye(t);n||(t!==a&&Ze(i,"get",t),Ze(i,"get",a));const{has:o}=Ni(i),s=r?uo:n?mo:gr;if(o.call(i,t))return s(e.get(t));if(o.call(i,a))return s(e.get(a));e!==i&&e.get(t)}function jr(e,t=!1){const n=this.__v_raw,r=ye(n),i=ye(e);return t||(e!==i&&Ze(r,"has",e),Ze(r,"has",i)),e===i?n.has(e):n.has(e)||n.has(i)}function Fr(e,t=!1){return e=e.__v_raw,!t&&Ze(ye(e),"iterate",mn),Reflect.get(e,"size",e)}function Zo(e){e=ye(e);const t=ye(this);return Ni(t).has.call(t,e)||(t.add(e),Ot(t,"add",e,e)),this}function es(e,t){t=ye(t);const n=ye(this),{has:r,get:i}=Ni(n);let a=r.call(n,e);a||(e=ye(e),a=r.call(n,e));const o=i.call(n,e);return n.set(e,t),a?hr(t,o)&&Ot(n,"set",e,t):Ot(n,"add",e,t),this}function ts(e){const t=ye(this),{has:n,get:r}=Ni(t);let i=n.call(t,e);i||(e=ye(e),i=n.call(t,e)),r&&r.call(t,e);const a=t.delete(e);return i&&Ot(t,"delete",e,void 0),a}function ns(){const e=ye(this),t=e.size!==0,n=e.clear();return t&&Ot(e,"clear",void 0,void 0),n}function Hr(e,t){return function(r,i){const a=this,o=a.__v_raw,s=ye(o),l=t?uo:e?mo:gr;return!e&&Ze(s,"iterate",mn),o.forEach((c,u)=>r.call(i,l(c),l(u),a))}}function Br(e,t,n){return function(...r){const i=this.__v_raw,a=ye(i),o=Sn(a),s=e==="entries"||e===Symbol.iterator&&o,l=e==="keys"&&o,c=i[e](...r),u=n?uo:t?mo:gr;return!t&&Ze(a,"iterate",l?ka:mn),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:s?[u(f[0]),u(f[1])]:u(f),done:d}},[Symbol.iterator](){return this}}}}function $t(e){return function(...t){return e==="delete"?!1:this}}function _d(){const e={get(a){return Dr(this,a)},get size(){return Fr(this)},has:jr,add:Zo,set:es,delete:ts,clear:ns,forEach:Hr(!1,!1)},t={get(a){return Dr(this,a,!1,!0)},get size(){return Fr(this)},has:jr,add:Zo,set:es,delete:ts,clear:ns,forEach:Hr(!1,!0)},n={get(a){return Dr(this,a,!0)},get size(){return Fr(this,!0)},has(a){return jr.call(this,a,!0)},add:$t("add"),set:$t("set"),delete:$t("delete"),clear:$t("clear"),forEach:Hr(!0,!1)},r={get(a){return Dr(this,a,!0,!0)},get size(){return Fr(this,!0)},has(a){return jr.call(this,a,!0)},add:$t("add"),set:$t("set"),delete:$t("delete"),clear:$t("clear"),forEach:Hr(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(a=>{e[a]=Br(a,!1,!1),n[a]=Br(a,!0,!1),t[a]=Br(a,!1,!0),r[a]=Br(a,!0,!0)}),[e,n,t,r]}const[yd,wd,kd,xd]=_d();function fo(e,t){const n=t?e?xd:kd:e?wd:yd;return(r,i,a)=>i==="__v_isReactive"?!e:i==="__v_isReadonly"?e:i==="__v_raw"?r:Reflect.get(ge(n,i)&&i in r?n:r,i,a)}const Ed={get:fo(!1,!1)},Cd={get:fo(!1,!0)},Od={get:fo(!0,!1)},yc=new WeakMap,wc=new WeakMap,kc=new WeakMap,Sd=new WeakMap;function Ad(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Td(e){return e.__v_skip||!Object.isExtensible(e)?0:Ad(Xf(e))}function Tr(e){return $n(e)?e:po(e,!1,_c,Ed,yc)}function Pd(e){return po(e,!1,bd,Cd,wc)}function Di(e){return po(e,!0,vd,Od,kc)}function po(e,t,n,r,i){if(!$e(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const a=i.get(e);if(a)return a;const o=Td(e);if(o===0)return e;const s=new Proxy(e,o===2?r:n);return i.set(e,s),s}function An(e){return $n(e)?An(e.__v_raw):!!(e&&e.__v_isReactive)}function $n(e){return!!(e&&e.__v_isReadonly)}function ui(e){return!!(e&&e.__v_isShallow)}function xc(e){return An(e)||$n(e)}function ye(e){const t=e&&e.__v_raw;return t?ye(t):e}function Ec(e){return ci(e,"__v_skip",!0),e}const gr=e=>$e(e)?Tr(e):e,mo=e=>$e(e)?Di(e):e;function Cc(e){Ut&&ot&&(e=ye(e),gc(e.dep||(e.dep=so())))}function Oc(e,t){e=ye(e),e.dep&&xa(e.dep)}function Be(e){return!!(e&&e.__v_isRef===!0)}function Ce(e){return Ac(e,!1)}function Sc(e){return Ac(e,!0)}function Ac(e,t){return Be(e)?e:new Id(e,t)}class Id{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:ye(t),this._value=n?t:gr(t)}get value(){return Cc(this),this._value}set value(t){const n=this.__v_isShallow||ui(t)||$n(t);t=n?t:ye(t),hr(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:gr(t),Oc(this))}}function ae(e){return Be(e)?e.value:e}const Md={get:(e,t,n)=>ae(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const i=e[t];return Be(i)&&!Be(n)?(i.value=n,!0):Reflect.set(e,t,n,r)}};function Tc(e){return An(e)?e:new Proxy(e,Md)}var Pc;class $d{constructor(t,n,r,i){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this[Pc]=!1,this._dirty=!0,this.effect=new lo(t,()=>{this._dirty||(this._dirty=!0,Oc(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!i,this.__v_isReadonly=r}get value(){const t=ye(this);return Cc(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}Pc="__v_isReadonly";function Ld(e,t,n=!1){let r,i;const a=fe(e);return a?(r=e,i=ut):(r=e.get,i=e.set),new $d(r,i,a||!i,n)}function Vt(e,t,n,r){let i;try{i=r?e(...r):e()}catch(a){ji(a,t,n)}return i}function rt(e,t,n,r){if(fe(e)){const a=Vt(e,t,n,r);return a&&cc(a)&&a.catch(o=>{ji(o,t,n)}),a}const i=[];for(let a=0;a>>1;br(Ue[r])vt&&Ue.splice(t,1)}function jd(e){oe(e)?Tn.push(...e):(!xt||!xt.includes(e,e.allowRecurse?sn+1:sn))&&Tn.push(e),Mc()}function rs(e,t=vr?vt+1:0){for(;tbr(n)-br(r)),sn=0;sne.id==null?1/0:e.id,Fd=(e,t)=>{const n=br(e)-br(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function $c(e){Ea=!1,vr=!0,Ue.sort(Fd);const t=ut;try{for(vt=0;vtg.trim())),f&&(i=n.map(dc))}let s,l=r[s=ra(t)]||r[s=ra(wt(t))];!l&&a&&(l=r[s=ra(Bn(t))]),l&&rt(l,e,6,i);const c=r[s+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[s])return;e.emitted[s]=!0,rt(c,e,6,i)}}function Lc(e,t,n=!1){const r=t.emitsCache,i=r.get(e);if(i!==void 0)return i;const a=e.emits;let o={},s=!1;if(!fe(e)){const l=c=>{const u=Lc(c,t,!0);u&&(s=!0,ze(o,u))};!n&&t.mixins.length&&t.mixins.forEach(l),e.extends&&l(e.extends),e.mixins&&e.mixins.forEach(l)}return!a&&!s?($e(e)&&r.set(e,null),null):(oe(a)?a.forEach(l=>o[l]=null):ze(o,a),$e(e)&&r.set(e,o),o)}function Fi(e,t){return!e||!Ar(t)?!1:(t=t.slice(2).replace(/Once$/,""),ge(e,t[0].toLowerCase()+t.slice(1))||ge(e,Bn(t))||ge(e,t))}let Ve=null,Hi=null;function di(e){const t=Ve;return Ve=e,Hi=e&&e.type.__scopeId||null,t}function Bd(e){Hi=e}function zd(){Hi=null}function _e(e,t=Ve,n){if(!t||e._n)return e;const r=(...i)=>{r._d&&hs(-1);const a=di(t),o=e(...i);return di(a),r._d&&hs(1),o};return r._n=!0,r._c=!0,r._d=!0,r}function aa(e){const{type:t,vnode:n,proxy:r,withProxy:i,props:a,propsOptions:[o],slots:s,attrs:l,emit:c,render:u,renderCache:f,data:d,setupState:g,ctx:m,inheritAttrs:y}=e;let k,h;const v=di(e);try{if(n.shapeFlag&4){const S=i||r;k=at(u.call(S,S,f,a,g,d,m)),h=l}else{const S=t;k=at(S.length>1?S(a,{attrs:l,slots:s,emit:c}):S(a,null)),h=t.props?l:Ud(l)}}catch(S){sr.length=0,ji(S,e,1),k=ee(Je)}let O=k;if(h&&y!==!1){const S=Object.keys(h),{shapeFlag:$}=O;S.length&&$&7&&(o&&S.some(ro)&&(h=Vd(h,o)),O=Yt(O,h))}return n.dirs&&(O=Yt(O),O.dirs=O.dirs?O.dirs.concat(n.dirs):n.dirs),n.transition&&(O.transition=n.transition),k=O,di(v),k}const Ud=e=>{let t;for(const n in e)(n==="class"||n==="style"||Ar(n))&&((t||(t={}))[n]=e[n]);return t},Vd=(e,t)=>{const n={};for(const r in e)(!ro(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function Wd(e,t,n){const{props:r,children:i,component:a}=e,{props:o,children:s,patchFlag:l}=t,c=a.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&l>=0){if(l&1024)return!0;if(l&16)return r?is(r,o,c):!!o;if(l&8){const u=t.dynamicProps;for(let f=0;fe.__isSuspense;function Rc(e,t){t&&t.pendingBranch?oe(e)?t.effects.push(...e):t.effects.push(e):jd(e)}function ri(e,t){if(He){let n=He.provides;const r=He.parent&&He.parent.provides;r===n&&(n=He.provides=Object.create(r)),n[e]=t}}function ft(e,t,n=!1){const r=He||Ve;if(r){const i=r.parent==null?r.vnode.appContext&&r.vnode.appContext.provides:r.parent.provides;if(i&&e in i)return i[e];if(arguments.length>1)return n&&fe(t)?t.call(r.proxy):t}}function Nc(e,t){return Bi(e,null,t)}function i2(e,t){return Bi(e,null,{flush:"post"})}const as={};function je(e,t,n){return Bi(e,t,n)}function Bi(e,t,{immediate:n,deep:r,flush:i,onTrack:a,onTrigger:o}=Te){const s=He;let l,c=!1,u=!1;if(Be(e)?(l=()=>e.value,c=ui(e)):An(e)?(l=()=>e,r=!0):oe(e)?(u=!0,c=e.some(h=>An(h)||ui(h)),l=()=>e.map(h=>{if(Be(h))return h.value;if(An(h))return un(h);if(fe(h))return Vt(h,s,2)})):fe(e)?t?l=()=>Vt(e,s,2):l=()=>{if(!(s&&s.isUnmounted))return f&&f(),rt(e,s,3,[d])}:l=ut,t&&r){const h=l;l=()=>un(h())}let f,d=h=>{f=k.onStop=()=>{Vt(h,s,4)}};if(wr)return d=ut,t?n&&rt(t,s,3,[l(),u?[]:void 0,d]):l(),ut;let g=u?[]:as;const m=()=>{if(k.active)if(t){const h=k.run();(r||c||(u?h.some((v,O)=>hr(v,g[O])):hr(h,g)))&&(f&&f(),rt(t,s,3,[h,g===as?void 0:g,d]),g=h)}else k.run()};m.allowRecurse=!!t;let y;i==="sync"?y=m:i==="post"?y=()=>qe(m,s&&s.suspense):(m.pre=!0,s&&(m.id=s.uid),y=()=>go(m));const k=new lo(l,y);return t?n?m():g=k.run():i==="post"?qe(k.run.bind(k),s&&s.suspense):k.run(),()=>{k.stop(),s&&s.scope&&io(s.scope.effects,k)}}function qd(e,t,n){const r=this.proxy,i=De(e)?e.includes(".")?Dc(r,e):()=>r[e]:e.bind(r,r);let a;fe(t)?a=t:(a=t.handler,n=t);const o=He;Ln(this);const s=Bi(i,a.bind(r),n);return o?Ln(o):hn(),s}function Dc(e,t){const n=t.split(".");return()=>{let r=e;for(let i=0;i{un(n,t)});else if(fc(e))for(const n in e)un(e[n],t);return e}function Gd(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Xt(()=>{e.isMounted=!0}),vo(()=>{e.isUnmounting=!0}),e}const tt=[Function,Array],Qd={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:tt,onEnter:tt,onAfterEnter:tt,onEnterCancelled:tt,onBeforeLeave:tt,onLeave:tt,onAfterLeave:tt,onLeaveCancelled:tt,onBeforeAppear:tt,onAppear:tt,onAfterAppear:tt,onAppearCancelled:tt},setup(e,{slots:t}){const n=xo(),r=Gd();let i;return()=>{const a=t.default&&Hc(t.default(),!0);if(!a||!a.length)return;let o=a[0];if(a.length>1){for(const y of a)if(y.type!==Je){o=y;break}}const s=ye(e),{mode:l}=s;if(r.isLeaving)return oa(o);const c=os(o);if(!c)return oa(o);const u=Ca(c,s,r,n);Oa(c,u);const f=n.subTree,d=f&&os(f);let g=!1;const{getTransitionKey:m}=c.type;if(m){const y=m();i===void 0?i=y:y!==i&&(i=y,g=!0)}if(d&&d.type!==Je&&(!ln(c,d)||g)){const y=Ca(d,s,r,n);if(Oa(d,y),l==="out-in")return r.isLeaving=!0,y.afterLeave=()=>{r.isLeaving=!1,n.update()},oa(o);l==="in-out"&&c.type!==Je&&(y.delayLeave=(k,h,v)=>{const O=Fc(r,d);O[String(d.key)]=d,k._leaveCb=()=>{h(),k._leaveCb=void 0,delete u.delayedLeave},u.delayedLeave=v})}return o}}},jc=Qd;function Fc(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function Ca(e,t,n,r){const{appear:i,mode:a,persisted:o=!1,onBeforeEnter:s,onEnter:l,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:f,onLeave:d,onAfterLeave:g,onLeaveCancelled:m,onBeforeAppear:y,onAppear:k,onAfterAppear:h,onAppearCancelled:v}=t,O=String(e.key),S=Fc(n,e),$=(_,x)=>{_&&rt(_,r,9,x)},I=(_,x)=>{const T=x[1];$(_,x),oe(_)?_.every(Y=>Y.length<=1)&&T():_.length<=1&&T()},w={mode:a,persisted:o,beforeEnter(_){let x=s;if(!n.isMounted)if(i)x=y||s;else return;_._leaveCb&&_._leaveCb(!0);const T=S[O];T&&ln(e,T)&&T.el._leaveCb&&T.el._leaveCb(),$(x,[_])},enter(_){let x=l,T=c,Y=u;if(!n.isMounted)if(i)x=k||l,T=h||c,Y=v||u;else return;let j=!1;const N=_._enterCb=C=>{j||(j=!0,C?$(Y,[_]):$(T,[_]),w.delayedLeave&&w.delayedLeave(),_._enterCb=void 0)};x?I(x,[_,N]):N()},leave(_,x){const T=String(e.key);if(_._enterCb&&_._enterCb(!0),n.isUnmounting)return x();$(f,[_]);let Y=!1;const j=_._leaveCb=N=>{Y||(Y=!0,x(),N?$(m,[_]):$(g,[_]),_._leaveCb=void 0,S[T]===e&&delete S[T])};S[T]=e,d?I(d,[_,j]):j()},clone(_){return Ca(_,t,n,r)}};return w}function oa(e){if(zi(e))return e=Yt(e),e.children=null,e}function os(e){return zi(e)?e.children?e.children[0]:void 0:e}function Oa(e,t){e.shapeFlag&6&&e.component?Oa(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 Hc(e,t=!1,n){let r=[],i=0;for(let a=0;a1)for(let a=0;a!!e.type.__asyncLoader,zi=e=>e.type.__isKeepAlive;function Bc(e,t){Uc(e,"a",t)}function zc(e,t){Uc(e,"da",t)}function Uc(e,t,n=He){const r=e.__wdc||(e.__wdc=()=>{let i=n;for(;i;){if(i.isDeactivated)return;i=i.parent}return e()});if(Ui(t,r,n),n){let i=n.parent;for(;i&&i.parent;)zi(i.parent.vnode)&&Xd(r,t,n,i),i=i.parent}}function Xd(e,t,n,r){const i=Ui(t,e,r,!0);Vc(()=>{io(r[t],i)},n)}function Ui(e,t,n=He,r=!1){if(n){const i=n[e]||(n[e]=[]),a=t.__weh||(t.__weh=(...o)=>{if(n.isUnmounted)return;zn(),Ln(n);const s=rt(t,n,e,o);return hn(),Un(),s});return r?i.unshift(a):i.push(a),a}}const It=e=>(t,n=He)=>(!wr||e==="sp")&&Ui(e,t,n),Jd=It("bm"),Xt=It("m"),Zd=It("bu"),ep=It("u"),vo=It("bum"),Vc=It("um"),tp=It("sp"),np=It("rtg"),rp=It("rtc");function ip(e,t=He){Ui("ec",e,t)}function pi(e,t){const n=Ve;if(n===null)return e;const r=Wi(n)||n.proxy,i=e.dirs||(e.dirs=[]);for(let a=0;at(o,s,void 0,a&&a[s]));else{const o=Object.keys(e);i=new Array(o.length);for(let s=0,l=o.length;svi(t)?!(t.type===Je||t.type===Le&&!qc(t.children)):!0)?e:null}const Sa=e=>e?ou(e)?Wi(e)||e.proxy:Sa(e.parent):null,mi=ze(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=>Sa(e.parent),$root:e=>Sa(e.root),$emit:e=>e.emit,$options:e=>_o(e),$forceUpdate:e=>e.f||(e.f=()=>go(e.update)),$nextTick:e=>e.n||(e.n=Qt.bind(e.proxy)),$watch:e=>qd.bind(e)}),op={get({_:e},t){const{ctx:n,setupState:r,data:i,props:a,accessCache:o,type:s,appContext:l}=e;let c;if(t[0]!=="$"){const g=o[t];if(g!==void 0)switch(g){case 1:return r[t];case 2:return i[t];case 4:return n[t];case 3:return a[t]}else{if(r!==Te&&ge(r,t))return o[t]=1,r[t];if(i!==Te&&ge(i,t))return o[t]=2,i[t];if((c=e.propsOptions[0])&&ge(c,t))return o[t]=3,a[t];if(n!==Te&&ge(n,t))return o[t]=4,n[t];Aa&&(o[t]=0)}}const u=mi[t];let f,d;if(u)return t==="$attrs"&&Ze(e,"get",t),u(e);if((f=s.__cssModules)&&(f=f[t]))return f;if(n!==Te&&ge(n,t))return o[t]=4,n[t];if(d=l.config.globalProperties,ge(d,t))return d[t]},set({_:e},t,n){const{data:r,setupState:i,ctx:a}=e;return i!==Te&&ge(i,t)?(i[t]=n,!0):r!==Te&&ge(r,t)?(r[t]=n,!0):ge(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(a[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:i,propsOptions:a}},o){let s;return!!n[o]||e!==Te&&ge(e,o)||t!==Te&&ge(t,o)||(s=a[0])&&ge(s,o)||ge(r,o)||ge(mi,o)||ge(i.config.globalProperties,o)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:ge(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};let Aa=!0;function sp(e){const t=_o(e),n=e.proxy,r=e.ctx;Aa=!1,t.beforeCreate&&ls(t.beforeCreate,e,"bc");const{data:i,computed:a,methods:o,watch:s,provide:l,inject:c,created:u,beforeMount:f,mounted:d,beforeUpdate:g,updated:m,activated:y,deactivated:k,beforeDestroy:h,beforeUnmount:v,destroyed:O,unmounted:S,render:$,renderTracked:I,renderTriggered:w,errorCaptured:_,serverPrefetch:x,expose:T,inheritAttrs:Y,components:j,directives:N,filters:C}=t;if(c&&lp(c,r,null,e.appContext.config.unwrapInjectedRef),o)for(const L in o){const V=o[L];fe(V)&&(r[L]=V.bind(n))}if(i){const L=i.call(n,n);$e(L)&&(e.data=Tr(L))}if(Aa=!0,a)for(const L in a){const V=a[L],J=fe(V)?V.bind(n,n):fe(V.get)?V.get.bind(n,n):ut,de=!fe(V)&&fe(V.set)?V.set.bind(n):ut,we=ce({get:J,set:de});Object.defineProperty(r,L,{enumerable:!0,configurable:!0,get:()=>we.value,set:ve=>we.value=ve})}if(s)for(const L in s)Gc(s[L],r,n,L);if(l){const L=fe(l)?l.call(n):l;Reflect.ownKeys(L).forEach(V=>{ri(V,L[V])})}u&&ls(u,e,"c");function z(L,V){oe(V)?V.forEach(J=>L(J.bind(n))):V&&L(V.bind(n))}if(z(Jd,f),z(Xt,d),z(Zd,g),z(ep,m),z(Bc,y),z(zc,k),z(ip,_),z(rp,I),z(np,w),z(vo,v),z(Vc,S),z(tp,x),oe(T))if(T.length){const L=e.exposed||(e.exposed={});T.forEach(V=>{Object.defineProperty(L,V,{get:()=>n[V],set:J=>n[V]=J})})}else e.exposed||(e.exposed={});$&&e.render===ut&&(e.render=$),Y!=null&&(e.inheritAttrs=Y),j&&(e.components=j),N&&(e.directives=N)}function lp(e,t,n=ut,r=!1){oe(e)&&(e=Ta(e));for(const i in e){const a=e[i];let o;$e(a)?"default"in a?o=ft(a.from||i,a.default,!0):o=ft(a.from||i):o=ft(a),Be(o)&&r?Object.defineProperty(t,i,{enumerable:!0,configurable:!0,get:()=>o.value,set:s=>o.value=s}):t[i]=o}}function ls(e,t,n){rt(oe(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function Gc(e,t,n,r){const i=r.includes(".")?Dc(n,r):()=>n[r];if(De(e)){const a=t[e];fe(a)&&je(i,a)}else if(fe(e))je(i,e.bind(n));else if($e(e))if(oe(e))e.forEach(a=>Gc(a,t,n,r));else{const a=fe(e.handler)?e.handler.bind(n):t[e.handler];fe(a)&&je(i,a,e)}}function _o(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:i,optionsCache:a,config:{optionMergeStrategies:o}}=e.appContext,s=a.get(t);let l;return s?l=s:!i.length&&!n&&!r?l=t:(l={},i.length&&i.forEach(c=>hi(l,c,o,!0)),hi(l,t,o)),$e(t)&&a.set(t,l),l}function hi(e,t,n,r=!1){const{mixins:i,extends:a}=t;a&&hi(e,a,n,!0),i&&i.forEach(o=>hi(e,o,n,!0));for(const o in t)if(!(r&&o==="expose")){const s=cp[o]||n&&n[o];e[o]=s?s(e[o],t[o]):t[o]}return e}const cp={data:cs,props:an,emits:an,methods:an,computed:an,beforeCreate:We,created:We,beforeMount:We,mounted:We,beforeUpdate:We,updated:We,beforeDestroy:We,beforeUnmount:We,destroyed:We,unmounted:We,activated:We,deactivated:We,errorCaptured:We,serverPrefetch:We,components:an,directives:an,watch:fp,provide:cs,inject:up};function cs(e,t){return t?e?function(){return ze(fe(e)?e.call(this,this):e,fe(t)?t.call(this,this):t)}:t:e}function up(e,t){return an(Ta(e),Ta(t))}function Ta(e){if(oe(e)){const t={};for(let n=0;n0)&&!(o&16)){if(o&8){const u=e.vnode.dynamicProps;for(let f=0;f{l=!0;const[d,g]=Xc(f,t,!0);ze(o,d),g&&s.push(...g)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!a&&!l)return $e(e)&&r.set(e,On),On;if(oe(a))for(let u=0;u-1,g[1]=y<0||m-1||ge(g,"default"))&&s.push(f)}}}const c=[o,s];return $e(e)&&r.set(e,c),c}function us(e){return e[0]!=="$"}function fs(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:e===null?"null":""}function ds(e,t){return fs(e)===fs(t)}function ps(e,t){return oe(t)?t.findIndex(n=>ds(n,e)):fe(t)&&ds(t,e)?0:-1}const Jc=e=>e[0]==="_"||e==="$stable",yo=e=>oe(e)?e.map(at):[at(e)],mp=(e,t,n)=>{if(t._n)return t;const r=_e((...i)=>yo(t(...i)),n);return r._c=!1,r},Zc=(e,t,n)=>{const r=e._ctx;for(const i in e){if(Jc(i))continue;const a=e[i];if(fe(a))t[i]=mp(i,a,r);else if(a!=null){const o=yo(a);t[i]=()=>o}}},eu=(e,t)=>{const n=yo(t);e.slots.default=()=>n},hp=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=ye(t),ci(t,"_",n)):Zc(t,e.slots={})}else e.slots={},t&&eu(e,t);ci(e.slots,Vi,1)},gp=(e,t,n)=>{const{vnode:r,slots:i}=e;let a=!0,o=Te;if(r.shapeFlag&32){const s=t._;s?n&&s===1?a=!1:(ze(i,t),!n&&s===1&&delete i._):(a=!t.$stable,Zc(t,i)),o=t}else t&&(eu(e,t),o={default:1});if(a)for(const s in i)!Jc(s)&&!(s in o)&&delete i[s]};function tu(){return{app:null,config:{isNativeTag:qf,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let vp=0;function bp(e,t){return function(r,i=null){fe(r)||(r=Object.assign({},r)),i!=null&&!$e(i)&&(i=null);const a=tu(),o=new Set;let s=!1;const l=a.app={_uid:vp++,_component:r,_props:i,_container:null,_context:a,_instance:null,version:lu,get config(){return a.config},set config(c){},use(c,...u){return o.has(c)||(c&&fe(c.install)?(o.add(c),c.install(l,...u)):fe(c)&&(o.add(c),c(l,...u))),l},mixin(c){return a.mixins.includes(c)||a.mixins.push(c),l},component(c,u){return u?(a.components[c]=u,l):a.components[c]},directive(c,u){return u?(a.directives[c]=u,l):a.directives[c]},mount(c,u,f){if(!s){const d=ee(r,i);return d.appContext=a,u&&t?t(d,c):e(d,c,f),s=!0,l._container=c,c.__vue_app__=l,Wi(d.component)||d.component.proxy}},unmount(){s&&(e(null,l._container),delete l._container.__vue_app__)},provide(c,u){return a.provides[c]=u,l}};return l}}function gi(e,t,n,r,i=!1){if(oe(e)){e.forEach((d,g)=>gi(d,t&&(oe(t)?t[g]:t),n,r,i));return}if(Pn(r)&&!i)return;const a=r.shapeFlag&4?Wi(r.component)||r.component.proxy:r.el,o=i?null:a,{i:s,r:l}=e,c=t&&t.r,u=s.refs===Te?s.refs={}:s.refs,f=s.setupState;if(c!=null&&c!==l&&(De(c)?(u[c]=null,ge(f,c)&&(f[c]=null)):Be(c)&&(c.value=null)),fe(l))Vt(l,s,12,[o,u]);else{const d=De(l),g=Be(l);if(d||g){const m=()=>{if(e.f){const y=d?u[l]:l.value;i?oe(y)&&io(y,a):oe(y)?y.includes(a)||y.push(a):d?(u[l]=[a],ge(f,l)&&(f[l]=u[l])):(l.value=[a],e.k&&(u[e.k]=l.value))}else d?(u[l]=o,ge(f,l)&&(f[l]=o)):g&&(l.value=o,e.k&&(u[e.k]=o))};o?(m.id=-1,qe(m,n)):m()}}}let Lt=!1;const zr=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Ur=e=>e.nodeType===8;function _p(e){const{mt:t,p:n,o:{patchProp:r,createText:i,nextSibling:a,parentNode:o,remove:s,insert:l,createComment:c}}=e,u=(h,v)=>{if(!v.hasChildNodes()){n(null,h,v),fi(),v._vnode=h;return}Lt=!1,f(v.firstChild,h,null,null,null),fi(),v._vnode=h,Lt&&console.error("Hydration completed but contains mismatches.")},f=(h,v,O,S,$,I=!1)=>{const w=Ur(h)&&h.data==="[",_=()=>y(h,v,O,S,$,w),{type:x,ref:T,shapeFlag:Y,patchFlag:j}=v,N=h.nodeType;v.el=h,j===-2&&(I=!1,v.dynamicChildren=null);let C=null;switch(x){case _r:N!==3?v.children===""?(l(v.el=i(""),o(h),h),C=h):C=_():(h.data!==v.children&&(Lt=!0,h.data=v.children),C=a(h));break;case Je:N!==8||w?C=_():C=a(h);break;case or:if(N!==1&&N!==3)C=_();else{C=h;const D=!v.children.length;for(let z=0;z{I=I||!!v.dynamicChildren;const{type:w,props:_,patchFlag:x,shapeFlag:T,dirs:Y}=v,j=w==="input"&&Y||w==="option";if(j||x!==-1){if(Y&>(v,null,O,"created"),_)if(j||!I||x&48)for(const C in _)(j&&C.endsWith("value")||Ar(C)&&!ir(C))&&r(h,C,null,_[C],!1,void 0,O);else _.onClick&&r(h,"onClick",null,_.onClick,!1,void 0,O);let N;if((N=_&&_.onVnodeBeforeMount)&&nt(N,O,v),Y&>(v,null,O,"beforeMount"),((N=_&&_.onVnodeMounted)||Y)&&Rc(()=>{N&&nt(N,O,v),Y&>(v,null,O,"mounted")},S),T&16&&!(_&&(_.innerHTML||_.textContent))){let C=g(h.firstChild,v,h,O,S,$,I);for(;C;){Lt=!0;const D=C;C=C.nextSibling,s(D)}}else T&8&&h.textContent!==v.children&&(Lt=!0,h.textContent=v.children)}return h.nextSibling},g=(h,v,O,S,$,I,w)=>{w=w||!!v.dynamicChildren;const _=v.children,x=_.length;for(let T=0;T{const{slotScopeIds:w}=v;w&&($=$?$.concat(w):w);const _=o(h),x=g(a(h),v,_,O,S,$,I);return x&&Ur(x)&&x.data==="]"?a(v.anchor=x):(Lt=!0,l(v.anchor=c("]"),_,x),x)},y=(h,v,O,S,$,I)=>{if(Lt=!0,v.el=null,I){const x=k(h);for(;;){const T=a(h);if(T&&T!==x)s(T);else break}}const w=a(h),_=o(h);return s(h),n(null,v,_,w,O,S,zr(_),$),w},k=h=>{let v=0;for(;h;)if(h=a(h),h&&Ur(h)&&(h.data==="["&&v++,h.data==="]")){if(v===0)return a(h);v--}return h};return[u,f]}const qe=Rc;function yp(e){return nu(e)}function wp(e){return nu(e,_p)}function nu(e,t){const n=ed();n.__VUE__=!0;const{insert:r,remove:i,patchProp:a,createElement:o,createText:s,createComment:l,setText:c,setElementText:u,parentNode:f,nextSibling:d,setScopeId:g=ut,cloneNode:m,insertStaticContent:y}=e,k=(p,b,E,M=null,P=null,B=null,W=!1,F=null,U=!!b.dynamicChildren)=>{if(p===b)return;p&&!ln(p,b)&&(M=G(p),xe(p,P,B,!0),p=null),b.patchFlag===-2&&(U=!1,b.dynamicChildren=null);const{type:R,ref:ne,shapeFlag:Q}=b;switch(R){case _r:h(p,b,E,M);break;case Je:v(p,b,E,M);break;case or:p==null&&O(b,E,M,W);break;case Le:N(p,b,E,M,P,B,W,F,U);break;default:Q&1?I(p,b,E,M,P,B,W,F,U):Q&6?C(p,b,E,M,P,B,W,F,U):(Q&64||Q&128)&&R.process(p,b,E,M,P,B,W,F,U,me)}ne!=null&&P&&gi(ne,p&&p.ref,B,b||p,!b)},h=(p,b,E,M)=>{if(p==null)r(b.el=s(b.children),E,M);else{const P=b.el=p.el;b.children!==p.children&&c(P,b.children)}},v=(p,b,E,M)=>{p==null?r(b.el=l(b.children||""),E,M):b.el=p.el},O=(p,b,E,M)=>{[p.el,p.anchor]=y(p.children,b,E,M,p.el,p.anchor)},S=({el:p,anchor:b},E,M)=>{let P;for(;p&&p!==b;)P=d(p),r(p,E,M),p=P;r(b,E,M)},$=({el:p,anchor:b})=>{let E;for(;p&&p!==b;)E=d(p),i(p),p=E;i(b)},I=(p,b,E,M,P,B,W,F,U)=>{W=W||b.type==="svg",p==null?w(b,E,M,P,B,W,F,U):T(p,b,P,B,W,F,U)},w=(p,b,E,M,P,B,W,F)=>{let U,R;const{type:ne,props:Q,shapeFlag:re,transition:le,patchFlag:be,dirs:Se}=p;if(p.el&&m!==void 0&&be===-1)U=p.el=m(p.el);else{if(U=p.el=o(p.type,B,Q&&Q.is,Q),re&8?u(U,p.children):re&16&&x(p.children,U,null,M,P,B&&ne!=="foreignObject",W,F),Se&>(p,null,M,"created"),Q){for(const Pe in Q)Pe!=="value"&&!ir(Pe)&&a(U,Pe,null,Q[Pe],B,p.children,M,P,H);"value"in Q&&a(U,"value",null,Q.value),(R=Q.onVnodeBeforeMount)&&nt(R,M,p)}_(U,p,p.scopeId,W,M)}Se&>(p,null,M,"beforeMount");const Ae=(!P||P&&!P.pendingBranch)&&le&&!le.persisted;Ae&&le.beforeEnter(U),r(U,b,E),((R=Q&&Q.onVnodeMounted)||Ae||Se)&&qe(()=>{R&&nt(R,M,p),Ae&&le.enter(U),Se&>(p,null,M,"mounted")},P)},_=(p,b,E,M,P)=>{if(E&&g(p,E),M)for(let B=0;B{for(let R=U;R{const F=b.el=p.el;let{patchFlag:U,dynamicChildren:R,dirs:ne}=b;U|=p.patchFlag&16;const Q=p.props||Te,re=b.props||Te;let le;E&&en(E,!1),(le=re.onVnodeBeforeUpdate)&&nt(le,E,b,p),ne&>(b,p,E,"beforeUpdate"),E&&en(E,!0);const be=P&&b.type!=="foreignObject";if(R?Y(p.dynamicChildren,R,F,E,M,be,B):W||J(p,b,F,null,E,M,be,B,!1),U>0){if(U&16)j(F,b,Q,re,E,M,P);else if(U&2&&Q.class!==re.class&&a(F,"class",null,re.class,P),U&4&&a(F,"style",Q.style,re.style,P),U&8){const Se=b.dynamicProps;for(let Ae=0;Ae{le&&nt(le,E,b,p),ne&>(b,p,E,"updated")},M)},Y=(p,b,E,M,P,B,W)=>{for(let F=0;F{if(E!==M){for(const F in M){if(ir(F))continue;const U=M[F],R=E[F];U!==R&&F!=="value"&&a(p,F,R,U,W,b.children,P,B,H)}if(E!==Te)for(const F in E)!ir(F)&&!(F in M)&&a(p,F,E[F],null,W,b.children,P,B,H);"value"in M&&a(p,"value",E.value,M.value)}},N=(p,b,E,M,P,B,W,F,U)=>{const R=b.el=p?p.el:s(""),ne=b.anchor=p?p.anchor:s("");let{patchFlag:Q,dynamicChildren:re,slotScopeIds:le}=b;le&&(F=F?F.concat(le):le),p==null?(r(R,E,M),r(ne,E,M),x(b.children,E,ne,P,B,W,F,U)):Q>0&&Q&64&&re&&p.dynamicChildren?(Y(p.dynamicChildren,re,E,P,B,W,F),(b.key!=null||P&&b===P.subTree)&&wo(p,b,!0)):J(p,b,E,ne,P,B,W,F,U)},C=(p,b,E,M,P,B,W,F,U)=>{b.slotScopeIds=F,p==null?b.shapeFlag&512?P.ctx.activate(b,E,M,W,U):D(b,E,M,P,B,W,U):z(p,b,U)},D=(p,b,E,M,P,B,W)=>{const F=p.component=Mp(p,M,P);if(zi(p)&&(F.ctx.renderer=me),$p(F),F.asyncDep){if(P&&P.registerDep(F,L),!p.el){const U=F.subTree=ee(Je);v(null,U,b,E)}return}L(F,p,b,E,P,B,W)},z=(p,b,E)=>{const M=b.component=p.component;if(Wd(p,b,E))if(M.asyncDep&&!M.asyncResolved){V(M,b,E);return}else M.next=b,Dd(M.update),M.update();else b.el=p.el,M.vnode=b},L=(p,b,E,M,P,B,W)=>{const F=()=>{if(p.isMounted){let{next:ne,bu:Q,u:re,parent:le,vnode:be}=p,Se=ne,Ae;en(p,!1),ne?(ne.el=be.el,V(p,ne,W)):ne=be,Q&&ia(Q),(Ae=ne.props&&ne.props.onVnodeBeforeUpdate)&&nt(Ae,le,ne,be),en(p,!0);const Pe=aa(p),it=p.subTree;p.subTree=Pe,k(it,Pe,f(it.el),G(it),p,P,B),ne.el=Pe.el,Se===null&&Yd(p,Pe.el),re&&qe(re,P),(Ae=ne.props&&ne.props.onVnodeUpdated)&&qe(()=>nt(Ae,le,ne,be),P)}else{let ne;const{el:Q,props:re}=b,{bm:le,m:be,parent:Se}=p,Ae=Pn(b);if(en(p,!1),le&&ia(le),!Ae&&(ne=re&&re.onVnodeBeforeMount)&&nt(ne,Se,b),en(p,!0),Q&&ie){const Pe=()=>{p.subTree=aa(p),ie(Q,p.subTree,p,P,null)};Ae?b.type.__asyncLoader().then(()=>!p.isUnmounted&&Pe()):Pe()}else{const Pe=p.subTree=aa(p);k(null,Pe,E,M,p,P,B),b.el=Pe.el}if(be&&qe(be,P),!Ae&&(ne=re&&re.onVnodeMounted)){const Pe=b;qe(()=>nt(ne,Se,Pe),P)}(b.shapeFlag&256||Se&&Pn(Se.vnode)&&Se.vnode.shapeFlag&256)&&p.a&&qe(p.a,P),p.isMounted=!0,b=E=M=null}},U=p.effect=new lo(F,()=>go(R),p.scope),R=p.update=()=>U.run();R.id=p.uid,en(p,!0),R()},V=(p,b,E)=>{b.component=p;const M=p.vnode.props;p.vnode=b,p.next=null,pp(p,b.props,M,E),gp(p,b.children,E),zn(),rs(),Un()},J=(p,b,E,M,P,B,W,F,U=!1)=>{const R=p&&p.children,ne=p?p.shapeFlag:0,Q=b.children,{patchFlag:re,shapeFlag:le}=b;if(re>0){if(re&128){we(R,Q,E,M,P,B,W,F,U);return}else if(re&256){de(R,Q,E,M,P,B,W,F,U);return}}le&8?(ne&16&&H(R,P,B),Q!==R&&u(E,Q)):ne&16?le&16?we(R,Q,E,M,P,B,W,F,U):H(R,P,B,!0):(ne&8&&u(E,""),le&16&&x(Q,E,M,P,B,W,F,U))},de=(p,b,E,M,P,B,W,F,U)=>{p=p||On,b=b||On;const R=p.length,ne=b.length,Q=Math.min(R,ne);let re;for(re=0;rene?H(p,P,B,!0,!1,Q):x(b,E,M,P,B,W,F,U,Q)},we=(p,b,E,M,P,B,W,F,U)=>{let R=0;const ne=b.length;let Q=p.length-1,re=ne-1;for(;R<=Q&&R<=re;){const le=p[R],be=b[R]=U?Ht(b[R]):at(b[R]);if(ln(le,be))k(le,be,E,null,P,B,W,F,U);else break;R++}for(;R<=Q&&R<=re;){const le=p[Q],be=b[re]=U?Ht(b[re]):at(b[re]);if(ln(le,be))k(le,be,E,null,P,B,W,F,U);else break;Q--,re--}if(R>Q){if(R<=re){const le=re+1,be=lere)for(;R<=Q;)xe(p[R],P,B,!0),R++;else{const le=R,be=R,Se=new Map;for(R=be;R<=re;R++){const Ge=b[R]=U?Ht(b[R]):at(b[R]);Ge.key!=null&&Se.set(Ge.key,R)}let Ae,Pe=0;const it=re-be+1;let wn=!1,Yo=0;const Gn=new Array(it);for(R=0;R=it){xe(Ge,P,B,!0);continue}let ht;if(Ge.key!=null)ht=Se.get(Ge.key);else for(Ae=be;Ae<=re;Ae++)if(Gn[Ae-be]===0&&ln(Ge,b[Ae])){ht=Ae;break}ht===void 0?xe(Ge,P,B,!0):(Gn[ht-be]=R+1,ht>=Yo?Yo=ht:wn=!0,k(Ge,b[ht],E,null,P,B,W,F,U),Pe++)}const Ko=wn?kp(Gn):On;for(Ae=Ko.length-1,R=it-1;R>=0;R--){const Ge=be+R,ht=b[Ge],qo=Ge+1{const{el:B,type:W,transition:F,children:U,shapeFlag:R}=p;if(R&6){ve(p.component.subTree,b,E,M);return}if(R&128){p.suspense.move(b,E,M);return}if(R&64){W.move(p,b,E,me);return}if(W===Le){r(B,b,E);for(let Q=0;QF.enter(B),P);else{const{leave:Q,delayLeave:re,afterLeave:le}=F,be=()=>r(B,b,E),Se=()=>{Q(B,()=>{be(),le&&le()})};re?re(B,be,Se):Se()}else r(B,b,E)},xe=(p,b,E,M=!1,P=!1)=>{const{type:B,props:W,ref:F,children:U,dynamicChildren:R,shapeFlag:ne,patchFlag:Q,dirs:re}=p;if(F!=null&&gi(F,null,E,p,!0),ne&256){b.ctx.deactivate(p);return}const le=ne&1&&re,be=!Pn(p);let Se;if(be&&(Se=W&&W.onVnodeBeforeUnmount)&&nt(Se,b,p),ne&6)q(p.component,E,M);else{if(ne&128){p.suspense.unmount(E,M);return}le&>(p,null,b,"beforeUnmount"),ne&64?p.type.remove(p,b,E,P,me,M):R&&(B!==Le||Q>0&&Q&64)?H(R,b,E,!1,!0):(B===Le&&Q&384||!P&&ne&16)&&H(U,b,E),M&&Ye(p)}(be&&(Se=W&&W.onVnodeUnmounted)||le)&&qe(()=>{Se&&nt(Se,b,p),le&>(p,null,b,"unmounted")},E)},Ye=p=>{const{type:b,el:E,anchor:M,transition:P}=p;if(b===Le){A(E,M);return}if(b===or){$(p);return}const B=()=>{i(E),P&&!P.persisted&&P.afterLeave&&P.afterLeave()};if(p.shapeFlag&1&&P&&!P.persisted){const{leave:W,delayLeave:F}=P,U=()=>W(E,B);F?F(p.el,B,U):U()}else B()},A=(p,b)=>{let E;for(;p!==b;)E=d(p),i(p),p=E;i(b)},q=(p,b,E)=>{const{bum:M,scope:P,update:B,subTree:W,um:F}=p;M&&ia(M),P.stop(),B&&(B.active=!1,xe(W,p,b,E)),F&&qe(F,b),qe(()=>{p.isUnmounted=!0},b),b&&b.pendingBranch&&!b.isUnmounted&&p.asyncDep&&!p.asyncResolved&&p.suspenseId===b.pendingId&&(b.deps--,b.deps===0&&b.resolve())},H=(p,b,E,M=!1,P=!1,B=0)=>{for(let W=B;Wp.shapeFlag&6?G(p.component.subTree):p.shapeFlag&128?p.suspense.next():d(p.anchor||p.el),se=(p,b,E)=>{p==null?b._vnode&&xe(b._vnode,null,null,!0):k(b._vnode||null,p,b,null,null,null,E),rs(),fi(),b._vnode=p},me={p:k,um:xe,m:ve,r:Ye,mt:D,mc:x,pc:J,pbc:Y,n:G,o:e};let ue,ie;return t&&([ue,ie]=t(me)),{render:se,hydrate:ue,createApp:bp(se,ue)}}function en({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function wo(e,t,n=!1){const r=e.children,i=t.children;if(oe(r)&&oe(i))for(let a=0;a>1,e[n[s]]0&&(t[r]=n[a-1]),n[a]=r)}}for(a=n.length,o=n[a-1];a-- >0;)n[a]=o,o=t[o];return n}const xp=e=>e.__isTeleport,ar=e=>e&&(e.disabled||e.disabled===""),ms=e=>typeof SVGElement<"u"&&e instanceof SVGElement,Ia=(e,t)=>{const n=e&&e.to;return De(n)?t?t(n):null:n},Ep={__isTeleport:!0,process(e,t,n,r,i,a,o,s,l,c){const{mc:u,pc:f,pbc:d,o:{insert:g,querySelector:m,createText:y,createComment:k}}=c,h=ar(t.props);let{shapeFlag:v,children:O,dynamicChildren:S}=t;if(e==null){const $=t.el=y(""),I=t.anchor=y("");g($,n,r),g(I,n,r);const w=t.target=Ia(t.props,m),_=t.targetAnchor=y("");w&&(g(_,w),o=o||ms(w));const x=(T,Y)=>{v&16&&u(O,T,Y,i,a,o,s,l)};h?x(n,I):w&&x(w,_)}else{t.el=e.el;const $=t.anchor=e.anchor,I=t.target=e.target,w=t.targetAnchor=e.targetAnchor,_=ar(e.props),x=_?n:I,T=_?$:w;if(o=o||ms(I),S?(d(e.dynamicChildren,S,x,i,a,o,s),wo(e,t,!0)):l||f(e,t,x,T,i,a,o,s,!1),h)_||Vr(t,n,$,c,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const Y=t.target=Ia(t.props,m);Y&&Vr(t,Y,null,c,0)}else _&&Vr(t,I,w,c,1)}},remove(e,t,n,r,{um:i,o:{remove:a}},o){const{shapeFlag:s,children:l,anchor:c,targetAnchor:u,target:f,props:d}=e;if(f&&a(u),(o||!ar(d))&&(a(c),s&16))for(let g=0;g0?lt||On:null,Sp(),yr>0&<&<.push(e),e}function Oe(e,t,n,r,i,a){return ru(Z(e,t,n,r,i,a,!0))}function ke(e,t,n,r,i){return ru(ee(e,t,n,r,i,!0))}function vi(e){return e?e.__v_isVNode===!0:!1}function ln(e,t){return e.type===t.type&&e.key===t.key}const Vi="__vInternal",iu=({key:e})=>e??null,ii=({ref:e,ref_key:t,ref_for:n})=>e!=null?De(e)||Be(e)||fe(e)?{i:Ve,r:e,k:t,f:!!n}:e:null;function Z(e,t=null,n=null,r=0,i=null,a=e===Le?0:1,o=!1,s=!1){const l={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&iu(t),ref:t&&ii(t),scopeId:Hi,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:a,patchFlag:r,dynamicProps:i,dynamicChildren:null,appContext:null};return s?(ko(l,n),a&128&&e.normalize(l)):n&&(l.shapeFlag|=De(n)?8:16),yr>0&&!o&<&&(l.patchFlag>0||a&6)&&l.patchFlag!==32&<.push(l),l}const ee=Ap;function Ap(e,t=null,n=null,r=0,i=null,a=!1){if((!e||e===Yc)&&(e=Je),vi(e)){const s=Yt(e,t,!0);return n&&ko(s,n),yr>0&&!a&<&&(s.shapeFlag&6?lt[lt.indexOf(e)]=s:lt.push(s)),s.patchFlag|=-2,s}if(jp(e)&&(e=e.__vccOpts),t){t=au(t);let{class:s,style:l}=t;s&&!De(s)&&(t.class=yt(s)),$e(l)&&(xc(l)&&!oe(l)&&(l=ze({},l)),t.style=Hn(l))}const o=De(e)?1:Kd(e)?128:xp(e)?64:$e(e)?4:fe(e)?2:0;return Z(e,t,n,r,i,o,a,!0)}function au(e){return e?xc(e)||Vi in e?ze({},e):e:null}function Yt(e,t,n=!1){const{props:r,ref:i,patchFlag:a,children:o}=e,s=t?Tp(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:s,key:s&&iu(s),ref:t&&t.ref?n&&i?oe(i)?i.concat(ii(t)):[i,ii(t)]:ii(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:o,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Le?a===-1?16:a|16:a,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Yt(e.ssContent),ssFallback:e.ssFallback&&Yt(e.ssFallback),el:e.el,anchor:e.anchor}}function Re(e=" ",t=0){return ee(_r,null,e,t)}function a2(e,t){const n=ee(or,null,e);return n.staticCount=t,n}function fn(e="",t=!1){return t?(te(),ke(Je,null,e)):ee(Je,null,e)}function at(e){return e==null||typeof e=="boolean"?ee(Je):oe(e)?ee(Le,null,e.slice()):typeof e=="object"?Ht(e):ee(_r,null,String(e))}function Ht(e){return e.el===null||e.memo?e:Yt(e)}function ko(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(oe(t))n=16;else if(typeof t=="object")if(r&65){const i=t.default;i&&(i._c&&(i._d=!1),ko(e,i()),i._c&&(i._d=!0));return}else{n=32;const i=t._;!i&&!(Vi in t)?t._ctx=Ve:i===3&&Ve&&(Ve.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else fe(t)?(t={default:t,_ctx:Ve},n=32):(t=String(t),r&64?(n=16,t=[Re(t)]):n=8);e.children=t,e.shapeFlag|=n}function Tp(...e){const t={};for(let n=0;nHe||Ve,Ln=e=>{He=e,e.scope.on()},hn=()=>{He&&He.scope.off(),He=null};function ou(e){return e.vnode.shapeFlag&4}let wr=!1;function $p(e,t=!1){wr=t;const{props:n,children:r}=e.vnode,i=ou(e);dp(e,n,i,t),hp(e,r);const a=i?Lp(e,t):void 0;return wr=!1,a}function Lp(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Ec(new Proxy(e.ctx,op));const{setup:r}=n;if(r){const i=e.setupContext=r.length>1?Np(e):null;Ln(e),zn();const a=Vt(r,e,0,[e.props,i]);if(Un(),hn(),cc(a)){if(a.then(hn,hn),t)return a.then(o=>{gs(e,o,t)}).catch(o=>{ji(o,e,0)});e.asyncDep=a}else gs(e,a,t)}else su(e,t)}function gs(e,t,n){fe(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:$e(t)&&(e.setupState=Tc(t)),su(e,n)}let vs;function su(e,t,n){const r=e.type;if(!e.render){if(!t&&vs&&!r.render){const i=r.template||_o(e).template;if(i){const{isCustomElement:a,compilerOptions:o}=e.appContext.config,{delimiters:s,compilerOptions:l}=r,c=ze(ze({isCustomElement:a,delimiters:s},o),l);r.render=vs(i,c)}}e.render=r.render||ut}Ln(e),zn(),sp(e),Un(),hn()}function Rp(e){return new Proxy(e.attrs,{get(t,n){return Ze(e,"get","$attrs"),t[n]}})}function Np(e){const t=r=>{e.exposed=r||{}};let n;return{get attrs(){return n||(n=Rp(e))},slots:e.slots,emit:e.emit,expose:t}}function Wi(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Tc(Ec(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in mi)return mi[n](e)}}))}function Dp(e,t=!0){return fe(e)?e.displayName||e.name:e.name||t&&e.__name}function jp(e){return fe(e)&&"__vccOpts"in e}const ce=(e,t)=>Ld(e,t,wr);function Pr(e,t,n){const r=arguments.length;return r===2?$e(t)&&!oe(t)?vi(t)?ee(e,null,[t]):ee(e,t):ee(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&vi(n)&&(n=[n]),ee(e,t,n))}const lu="3.2.39",Fp="http://www.w3.org/2000/svg",cn=typeof document<"u"?document:null,bs=cn&&cn.createElement("template"),Hp={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 i=t?cn.createElementNS(Fp,e):cn.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&i.setAttribute("multiple",r.multiple),i},createText:e=>cn.createTextNode(e),createComment:e=>cn.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>cn.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},cloneNode(e){const t=e.cloneNode(!0);return"_value"in e&&(t._value=e._value),t},insertStaticContent(e,t,n,r,i,a){const o=n?n.previousSibling:t.lastChild;if(i&&(i===a||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{bs.innerHTML=r?`${e}`:e;const s=bs.content;if(r){const l=s.firstChild;for(;l.firstChild;)s.appendChild(l.firstChild);s.removeChild(l)}t.insertBefore(s,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function Bp(e,t,n){const r=e._vtc;r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function zp(e,t,n){const r=e.style,i=De(n);if(n&&!i){for(const a in n)Ma(r,a,n[a]);if(t&&!De(t))for(const a in t)n[a]==null&&Ma(r,a,"")}else{const a=r.display;i?t!==n&&(r.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(r.display=a)}}const _s=/\s*!important$/;function Ma(e,t,n){if(oe(n))n.forEach(r=>Ma(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=Up(e,t);_s.test(n)?e.setProperty(Bn(r),n.replace(_s,""),"important"):e[r]=n}}const ys=["Webkit","Moz","ms"],sa={};function Up(e,t){const n=sa[t];if(n)return n;let r=wt(t);if(r!=="filter"&&r in e)return sa[t]=r;r=Ri(r);for(let i=0;i{let e=Date.now,t=!1;if(typeof window<"u"){Date.now()>document.createEvent("Event").timeStamp&&(e=performance.now.bind(performance));const n=navigator.userAgent.match(/firefox\/(\d+)/i);t=!!(n&&Number(n[1])<=53)}return[e,t]})();let $a=0;const Kp=Promise.resolve(),qp=()=>{$a=0},Gp=()=>$a||(Kp.then(qp),$a=cu());function Qp(e,t,n,r){e.addEventListener(t,n,r)}function Xp(e,t,n,r){e.removeEventListener(t,n,r)}function Jp(e,t,n,r,i=null){const a=e._vei||(e._vei={}),o=a[t];if(r&&o)o.value=r;else{const[s,l]=Zp(t);if(r){const c=a[t]=em(r,i);Qp(e,s,c,l)}else o&&(Xp(e,s,o,l),a[t]=void 0)}}const ks=/(?:Once|Passive|Capture)$/;function Zp(e){let t;if(ks.test(e)){t={};let r;for(;r=e.match(ks);)e=e.slice(0,e.length-r[0].length),t[r[0].toLowerCase()]=!0}return[e[2]===":"?e.slice(3):Bn(e.slice(2)),t]}function em(e,t){const n=r=>{const i=r.timeStamp||cu();(Yp||i>=n.attached-1)&&rt(tm(r,n.value),t,5,[r])};return n.value=e,n.attached=Gp(),n}function tm(e,t){if(oe(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>i=>!i._stopped&&r&&r(i))}else return t}const xs=/^on[a-z]/,nm=(e,t,n,r,i=!1,a,o,s,l)=>{t==="class"?Bp(e,r,i):t==="style"?zp(e,n,r):Ar(t)?ro(t)||Jp(e,t,n,r,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):rm(e,t,r,i))?Wp(e,t,r,a,o,s,l):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),Vp(e,t,r,i))};function rm(e,t,n,r){return r?!!(t==="innerHTML"||t==="textContent"||t in e&&xs.test(t)&&fe(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||xs.test(t)&&De(n)?!1:t in e}const Rt="transition",Qn="animation",Yi=(e,{slots:t})=>Pr(jc,im(e),t);Yi.displayName="Transition";const uu={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};Yi.props=ze({},jc.props,uu);const tn=(e,t=[])=>{oe(e)?e.forEach(n=>n(...t)):e&&e(...t)},Es=e=>e?oe(e)?e.some(t=>t.length>1):e.length>1:!1;function im(e){const t={};for(const j in e)j in uu||(t[j]=e[j]);if(e.css===!1)return t;const{name:n="v",type:r,duration:i,enterFromClass:a=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:l=a,appearActiveClass:c=o,appearToClass:u=s,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:d=`${n}-leave-active`,leaveToClass:g=`${n}-leave-to`}=e,m=am(i),y=m&&m[0],k=m&&m[1],{onBeforeEnter:h,onEnter:v,onEnterCancelled:O,onLeave:S,onLeaveCancelled:$,onBeforeAppear:I=h,onAppear:w=v,onAppearCancelled:_=O}=t,x=(j,N,C)=>{nn(j,N?u:s),nn(j,N?c:o),C&&C()},T=(j,N)=>{j._isLeaving=!1,nn(j,f),nn(j,g),nn(j,d),N&&N()},Y=j=>(N,C)=>{const D=j?w:v,z=()=>x(N,j,C);tn(D,[N,z]),Cs(()=>{nn(N,j?l:a),Nt(N,j?u:s),Es(D)||Os(N,r,y,z)})};return ze(t,{onBeforeEnter(j){tn(h,[j]),Nt(j,a),Nt(j,o)},onBeforeAppear(j){tn(I,[j]),Nt(j,l),Nt(j,c)},onEnter:Y(!1),onAppear:Y(!0),onLeave(j,N){j._isLeaving=!0;const C=()=>T(j,N);Nt(j,f),lm(),Nt(j,d),Cs(()=>{j._isLeaving&&(nn(j,f),Nt(j,g),Es(S)||Os(j,r,k,C))}),tn(S,[j,C])},onEnterCancelled(j){x(j,!1),tn(O,[j])},onAppearCancelled(j){x(j,!0),tn(_,[j])},onLeaveCancelled(j){T(j),tn($,[j])}})}function am(e){if(e==null)return null;if($e(e))return[la(e.enter),la(e.leave)];{const t=la(e);return[t,t]}}function la(e){return dc(e)}function Nt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function nn(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Cs(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let om=0;function Os(e,t,n,r){const i=e._endId=++om,a=()=>{i===e._endId&&r()};if(n)return setTimeout(a,n);const{type:o,timeout:s,propCount:l}=sm(e,t);if(!o)return r();const c=o+"end";let u=0;const f=()=>{e.removeEventListener(c,d),a()},d=g=>{g.target===e&&++u>=l&&f()};setTimeout(()=>{u(n[m]||"").split(", "),i=r(Rt+"Delay"),a=r(Rt+"Duration"),o=Ss(i,a),s=r(Qn+"Delay"),l=r(Qn+"Duration"),c=Ss(s,l);let u=null,f=0,d=0;t===Rt?o>0&&(u=Rt,f=o,d=a.length):t===Qn?c>0&&(u=Qn,f=c,d=l.length):(f=Math.max(o,c),u=f>0?o>c?Rt:Qn:null,d=u?u===Rt?a.length:l.length:0);const g=u===Rt&&/\b(transform|all)(,|$)/.test(n[Rt+"Property"]);return{type:u,timeout:f,propCount:d,hasTransform:g}}function Ss(e,t){for(;e.lengthAs(n)+As(e[r])))}function As(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function lm(){return document.body.offsetHeight}const cm=["ctrl","shift","alt","meta"],um={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>cm.some(n=>e[`${n}Key`]&&!t.includes(n))},fu=(e,t)=>(n,...r)=>{for(let i=0;i{Xn(e,!1)}):Xn(e,t))},beforeUnmount(e,{value:t}){Xn(e,t)}};function Xn(e,t){e.style.display=t?e._vod:"none"}const du=ze({patchProp:nm},Hp);let lr,Ts=!1;function fm(){return lr||(lr=yp(du))}function dm(){return lr=Ts?lr:wp(du),Ts=!0,lr}const pm=(...e)=>{const t=fm().createApp(...e),{mount:n}=t;return t.mount=r=>{const i=pu(r);if(!i)return;const a=t._component;!fe(a)&&!a.render&&!a.template&&(a.template=i.innerHTML),i.innerHTML="";const o=n(i,!1,i instanceof SVGElement);return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),o},t},mm=(...e)=>{const t=dm().createApp(...e),{mount:n}=t;return t.mount=r=>{const i=pu(r);if(i)return n(i,!0,i instanceof SVGElement)},t};function pu(e){return De(e)?document.querySelector(e):e}/*! + * vue-router v4.1.5 + * (c) 2022 Eduardo San Martin Morote + * @license MIT + */const xn=typeof window<"u";function hm(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const Ee=Object.assign;function ca(e,t){const n={};for(const r in t){const i=t[r];n[r]=dt(i)?i.map(e):e(i)}return n}const cr=()=>{},dt=Array.isArray,gm=/\/$/,vm=e=>e.replace(gm,"");function ua(e,t,n="/"){let r,i={},a="",o="";const s=t.indexOf("#");let l=t.indexOf("?");return s=0&&(l=-1),l>-1&&(r=t.slice(0,l),a=t.slice(l+1,s>-1?s:t.length),i=e(a)),s>-1&&(r=r||t.slice(0,s),o=t.slice(s,t.length)),r=wm(r??t,n),{fullPath:r+(a&&"?")+a+o,path:r,query:i,hash:o}}function bm(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Ps(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function _m(e,t,n){const r=t.matched.length-1,i=n.matched.length-1;return r>-1&&r===i&&Rn(t.matched[r],n.matched[i])&&mu(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Rn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function mu(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!ym(e[n],t[n]))return!1;return!0}function ym(e,t){return dt(e)?Is(e,t):dt(t)?Is(t,e):e===t}function Is(e,t){return dt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function wm(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let i=n.length-1,a,o;for(a=0;a1&&i--;else break;return n.slice(0,i).join("/")+"/"+r.slice(a-(a===r.length?1:0)).join("/")}var Nn;(function(e){e.pop="pop",e.push="push"})(Nn||(Nn={}));var gn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(gn||(gn={}));const fa="";function hu(e){if(!e)if(xn){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),vm(e)}const km=/^[^#]+#/;function gu(e,t){return e.replace(km,"#")+t}function xm(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 Ki=()=>({left:window.pageXOffset,top:window.pageYOffset});function Em(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),i=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!i)return;t=xm(i,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Ms(e,t){return(history.state?history.state.position-t:-1)+e}const La=new Map;function Cm(e,t){La.set(e,t)}function Om(e){const t=La.get(e);return La.delete(e),t}let Sm=()=>location.protocol+"//"+location.host;function vu(e,t){const{pathname:n,search:r,hash:i}=t,a=e.indexOf("#");if(a>-1){let s=i.includes(e.slice(a))?e.slice(a).length:1,l=i.slice(s);return l[0]!=="/"&&(l="/"+l),Ps(l,"")}return Ps(n,e)+r+i}function Am(e,t,n,r){let i=[],a=[],o=null;const s=({state:d})=>{const g=vu(e,location),m=n.value,y=t.value;let k=0;if(d){if(n.value=g,t.value=d,o&&o===m){o=null;return}k=y?d.position-y.position:0}else r(g);i.forEach(h=>{h(n.value,m,{delta:k,type:Nn.pop,direction:k?k>0?gn.forward:gn.back:gn.unknown})})};function l(){o=n.value}function c(d){i.push(d);const g=()=>{const m=i.indexOf(d);m>-1&&i.splice(m,1)};return a.push(g),g}function u(){const{history:d}=window;d.state&&d.replaceState(Ee({},d.state,{scroll:Ki()}),"")}function f(){for(const d of a)d();a=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",u),{pauseListeners:l,listen:c,destroy:f}}function $s(e,t,n,r=!1,i=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:i?Ki():null}}function Tm(e){const{history:t,location:n}=window,r={value:vu(e,n)},i={value:t.state};i.value||a(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(l,c,u){const f=e.indexOf("#"),d=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+l:Sm()+e+l;try{t[u?"replaceState":"pushState"](c,"",d),i.value=c}catch(g){console.error(g),n[u?"replace":"assign"](d)}}function o(l,c){const u=Ee({},t.state,$s(i.value.back,l,i.value.forward,!0),c,{position:i.value.position});a(l,u,!0),r.value=l}function s(l,c){const u=Ee({},i.value,t.state,{forward:l,scroll:Ki()});a(u.current,u,!0);const f=Ee({},$s(r.value,l,null),{position:u.position+1},c);a(l,f,!1),r.value=l}return{location:r,state:i,push:s,replace:o}}function Pm(e){e=hu(e);const t=Tm(e),n=Am(e,t.state,t.location,t.replace);function r(a,o=!0){o||n.pauseListeners(),history.go(a)}const i=Ee({location:"",base:e,go:r,createHref:gu.bind(null,e)},t,n);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}function Im(e=""){let t=[],n=[fa],r=0;e=hu(e);function i(s){r++,r===n.length||n.splice(r),n.push(s)}function a(s,l,{direction:c,delta:u}){const f={direction:c,delta:u,type:Nn.pop};for(const d of t)d(s,l,f)}const o={location:fa,state:{},base:e,createHref:gu.bind(null,e),replace(s){n.splice(r--,1),i(s)},push(s,l){i(s)},listen(s){return t.push(s),()=>{const l=t.indexOf(s);l>-1&&t.splice(l,1)}},destroy(){t=[],n=[fa],r=0},go(s,l=!0){const c=this.location,u=s<0?gn.back:gn.forward;r=Math.max(0,Math.min(r+s,n.length-1)),l&&a(this.location,c,{direction:u,delta:s})}};return Object.defineProperty(o,"location",{enumerable:!0,get:()=>n[r]}),o}function Mm(e){return typeof e=="string"||e&&typeof e=="object"}function bu(e){return typeof e=="string"||typeof e=="symbol"}const Dt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},_u=Symbol("");var Ls;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Ls||(Ls={}));function Dn(e,t){return Ee(new Error,{type:e,[_u]:!0},t)}function kt(e,t){return e instanceof Error&&_u in e&&(t==null||!!(e.type&t))}const Rs="[^/]+?",$m={sensitive:!1,strict:!1,start:!0,end:!0},Lm=/[.+*?^${}()[\]/\\]/g;function Rm(e,t){const n=Ee({},$m,t),r=[];let i=n.start?"^":"";const a=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(i+="/");for(let f=0;ft.length?t.length===1&&t[0]===40+40?1:-1:0}function Dm(e,t){let n=0;const r=e.score,i=t.score;for(;n0&&t[t.length-1]<0}const jm={type:0,value:""},Fm=/[a-zA-Z0-9_]/;function Hm(e){if(!e)return[[]];if(e==="/")return[[jm]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(g){throw new Error(`ERR (${n})/"${c}": ${g}`)}let n=0,r=n;const i=[];let a;function o(){a&&i.push(a),a=[]}let s=0,l,c="",u="";function f(){c&&(n===0?a.push({type:0,value:c}):n===1||n===2||n===3?(a.length>1&&(l==="*"||l==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),a.push({type:1,value:c,regexp:u,repeatable:l==="*"||l==="+",optional:l==="*"||l==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=l}for(;s{o(v)}:cr}function o(u){if(bu(u)){const f=r.get(u);f&&(r.delete(u),n.splice(n.indexOf(f),1),f.children.forEach(o),f.alias.forEach(o))}else{const f=n.indexOf(u);f>-1&&(n.splice(f,1),u.record.name&&r.delete(u.record.name),u.children.forEach(o),u.alias.forEach(o))}}function s(){return n}function l(u){let f=0;for(;f=0&&(u.record.path!==n[f].record.path||!yu(u,n[f]));)f++;n.splice(f,0,u),u.record.name&&!js(u)&&r.set(u.record.name,u)}function c(u,f){let d,g={},m,y;if("name"in u&&u.name){if(d=r.get(u.name),!d)throw Dn(1,{location:u});y=d.record.name,g=Ee(Ds(f.params,d.keys.filter(v=>!v.optional).map(v=>v.name)),u.params&&Ds(u.params,d.keys.map(v=>v.name))),m=d.stringify(g)}else if("path"in u)m=u.path,d=n.find(v=>v.re.test(m)),d&&(g=d.parse(m),y=d.record.name);else{if(d=f.name?r.get(f.name):n.find(v=>v.re.test(f.path)),!d)throw Dn(1,{location:u,currentLocation:f});y=d.record.name,g=Ee({},f.params,u.params),m=d.stringify(g)}const k=[];let h=d;for(;h;)k.unshift(h.record),h=h.parent;return{name:y,path:m,params:g,matched:k,meta:Wm(k)}}return e.forEach(u=>a(u)),{addRoute:a,resolve:c,removeRoute:o,getRoutes:s,getRecordMatcher:i}}function Ds(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function Um(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:Vm(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 Vm(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=="boolean"?n:n[r];return t}function js(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function Wm(e){return e.reduce((t,n)=>Ee(t,n.meta),{})}function Fs(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function yu(e,t){return t.children.some(n=>n===e||yu(e,n))}const wu=/#/g,Ym=/&/g,Km=/\//g,qm=/=/g,Gm=/\?/g,ku=/\+/g,Qm=/%5B/g,Xm=/%5D/g,xu=/%5E/g,Jm=/%60/g,Eu=/%7B/g,Zm=/%7C/g,Cu=/%7D/g,eh=/%20/g;function Eo(e){return encodeURI(""+e).replace(Zm,"|").replace(Qm,"[").replace(Xm,"]")}function th(e){return Eo(e).replace(Eu,"{").replace(Cu,"}").replace(xu,"^")}function Ra(e){return Eo(e).replace(ku,"%2B").replace(eh,"+").replace(wu,"%23").replace(Ym,"%26").replace(Jm,"`").replace(Eu,"{").replace(Cu,"}").replace(xu,"^")}function nh(e){return Ra(e).replace(qm,"%3D")}function rh(e){return Eo(e).replace(wu,"%23").replace(Gm,"%3F")}function ih(e){return e==null?"":rh(e).replace(Km,"%2F")}function _i(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function ah(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let i=0;ia&&Ra(a)):[r&&Ra(r)]).forEach(a=>{a!==void 0&&(t+=(t.length?"&":"")+n,a!=null&&(t+="="+a))})}return t}function oh(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=dt(r)?r.map(i=>i==null?null:""+i):r==null?r:""+r)}return t}const sh=Symbol(""),Bs=Symbol(""),qi=Symbol(""),Co=Symbol(""),Na=Symbol("");function Jn(){let e=[];function t(r){return e.push(r),()=>{const i=e.indexOf(r);i>-1&&e.splice(i,1)}}function n(){e=[]}return{add:t,list:()=>e,reset:n}}function Bt(e,t,n,r,i){const a=r&&(r.enterCallbacks[i]=r.enterCallbacks[i]||[]);return()=>new Promise((o,s)=>{const l=f=>{f===!1?s(Dn(4,{from:n,to:t})):f instanceof Error?s(f):Mm(f)?s(Dn(2,{from:t,to:f})):(a&&r.enterCallbacks[i]===a&&typeof f=="function"&&a.push(f),o())},c=e.call(r&&r.instances[i],t,n,l);let u=Promise.resolve(c);e.length<3&&(u=u.then(l)),u.catch(f=>s(f))})}function da(e,t,n,r){const i=[];for(const a of e)for(const o in a.components){let s=a.components[o];if(!(t!=="beforeRouteEnter"&&!a.instances[o]))if(lh(s)){const c=(s.__vccOpts||s)[t];c&&i.push(Bt(c,n,r,a,o))}else{let l=s();i.push(()=>l.then(c=>{if(!c)return Promise.reject(new Error(`Couldn't resolve component "${o}" at "${a.path}"`));const u=hm(c)?c.default:c;a.components[o]=u;const d=(u.__vccOpts||u)[t];return d&&Bt(d,n,r,a,o)()}))}}return i}function lh(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function zs(e){const t=ft(qi),n=ft(Co),r=ce(()=>t.resolve(ae(e.to))),i=ce(()=>{const{matched:l}=r.value,{length:c}=l,u=l[c-1],f=n.matched;if(!u||!f.length)return-1;const d=f.findIndex(Rn.bind(null,u));if(d>-1)return d;const g=Us(l[c-2]);return c>1&&Us(u)===g&&f[f.length-1].path!==g?f.findIndex(Rn.bind(null,l[c-2])):d}),a=ce(()=>i.value>-1&&fh(n.params,r.value.params)),o=ce(()=>i.value>-1&&i.value===n.matched.length-1&&mu(n.params,r.value.params));function s(l={}){return uh(l)?t[ae(e.replace)?"replace":"push"](ae(e.to)).catch(cr):Promise.resolve()}return{route:r,href:ce(()=>r.value.href),isActive:a,isExactActive:o,navigate:s}}const ch=he({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:zs,setup(e,{slots:t}){const n=Tr(zs(e)),{options:r}=ft(qi),i=ce(()=>({[Vs(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[Vs(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const a=t.default&&t.default(n);return e.custom?a:Pr("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:i.value},a)}}}),Ou=ch;function uh(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 fh(e,t){for(const n in t){const r=t[n],i=e[n];if(typeof r=="string"){if(r!==i)return!1}else if(!dt(i)||i.length!==r.length||r.some((a,o)=>a!==i[o]))return!1}return!0}function Us(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Vs=(e,t,n)=>e??t??n,dh=he({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=ft(Na),i=ce(()=>e.route||r.value),a=ft(Bs,0),o=ce(()=>{let c=ae(a);const{matched:u}=i.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),s=ce(()=>i.value.matched[o.value]);ri(Bs,ce(()=>o.value+1)),ri(sh,s),ri(Na,i);const l=Ce();return je(()=>[l.value,s.value,e.name],([c,u,f],[d,g,m])=>{u&&(u.instances[f]=c,g&&g!==u&&c&&c===d&&(u.leaveGuards.size||(u.leaveGuards=g.leaveGuards),u.updateGuards.size||(u.updateGuards=g.updateGuards))),c&&u&&(!g||!Rn(u,g)||!d)&&(u.enterCallbacks[f]||[]).forEach(y=>y(c))},{flush:"post"}),()=>{const c=i.value,u=e.name,f=s.value,d=f&&f.components[u];if(!d)return Ws(n.default,{Component:d,route:c});const g=f.props[u],m=g?g===!0?c.params:typeof g=="function"?g(c):g:null,k=Pr(d,Ee({},m,t,{onVnodeUnmounted:h=>{h.component.isUnmounted&&(f.instances[u]=null)},ref:l}));return Ws(n.default,{Component:k,route:c})||k}}});function Ws(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const ph=dh;function mh(e){const t=zm(e.routes,e),n=e.parseQuery||ah,r=e.stringifyQuery||Hs,i=e.history,a=Jn(),o=Jn(),s=Jn(),l=Sc(Dt);let c=Dt;xn&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=ca.bind(null,A=>""+A),f=ca.bind(null,ih),d=ca.bind(null,_i);function g(A,q){let H,G;return bu(A)?(H=t.getRecordMatcher(A),G=q):G=A,t.addRoute(G,H)}function m(A){const q=t.getRecordMatcher(A);q&&t.removeRoute(q)}function y(){return t.getRoutes().map(A=>A.record)}function k(A){return!!t.getRecordMatcher(A)}function h(A,q){if(q=Ee({},q||l.value),typeof A=="string"){const ie=ua(n,A,q.path),p=t.resolve({path:ie.path},q),b=i.createHref(ie.fullPath);return Ee(ie,p,{params:d(p.params),hash:_i(ie.hash),redirectedFrom:void 0,href:b})}let H;if("path"in A)H=Ee({},A,{path:ua(n,A.path,q.path).path});else{const ie=Ee({},A.params);for(const p in ie)ie[p]==null&&delete ie[p];H=Ee({},A,{params:f(A.params)}),q.params=f(q.params)}const G=t.resolve(H,q),se=A.hash||"";G.params=u(d(G.params));const me=bm(r,Ee({},A,{hash:th(se),path:G.path})),ue=i.createHref(me);return Ee({fullPath:me,hash:se,query:r===Hs?oh(A.query):A.query||{}},G,{redirectedFrom:void 0,href:ue})}function v(A){return typeof A=="string"?ua(n,A,l.value.path):Ee({},A)}function O(A,q){if(c!==A)return Dn(8,{from:q,to:A})}function S(A){return w(A)}function $(A){return S(Ee(v(A),{replace:!0}))}function I(A){const q=A.matched[A.matched.length-1];if(q&&q.redirect){const{redirect:H}=q;let G=typeof H=="function"?H(A):H;return typeof G=="string"&&(G=G.includes("?")||G.includes("#")?G=v(G):{path:G},G.params={}),Ee({query:A.query,hash:A.hash,params:"path"in G?{}:A.params},G)}}function w(A,q){const H=c=h(A),G=l.value,se=A.state,me=A.force,ue=A.replace===!0,ie=I(H);if(ie)return w(Ee(v(ie),{state:typeof ie=="object"?Ee({},se,ie.state):se,force:me,replace:ue}),q||H);const p=H;p.redirectedFrom=q;let b;return!me&&_m(r,G,H)&&(b=Dn(16,{to:p,from:G}),de(G,G,!0,!1)),(b?Promise.resolve(b):x(p,G)).catch(E=>kt(E)?kt(E,2)?E:J(E):L(E,p,G)).then(E=>{if(E){if(kt(E,2))return w(Ee({replace:ue},v(E.to),{state:typeof E.to=="object"?Ee({},se,E.to.state):se,force:me}),q||p)}else E=Y(p,G,!0,ue,se);return T(p,G,E),E})}function _(A,q){const H=O(A,q);return H?Promise.reject(H):Promise.resolve()}function x(A,q){let H;const[G,se,me]=hh(A,q);H=da(G.reverse(),"beforeRouteLeave",A,q);for(const ie of G)ie.leaveGuards.forEach(p=>{H.push(Bt(p,A,q))});const ue=_.bind(null,A,q);return H.push(ue),kn(H).then(()=>{H=[];for(const ie of a.list())H.push(Bt(ie,A,q));return H.push(ue),kn(H)}).then(()=>{H=da(se,"beforeRouteUpdate",A,q);for(const ie of se)ie.updateGuards.forEach(p=>{H.push(Bt(p,A,q))});return H.push(ue),kn(H)}).then(()=>{H=[];for(const ie of A.matched)if(ie.beforeEnter&&!q.matched.includes(ie))if(dt(ie.beforeEnter))for(const p of ie.beforeEnter)H.push(Bt(p,A,q));else H.push(Bt(ie.beforeEnter,A,q));return H.push(ue),kn(H)}).then(()=>(A.matched.forEach(ie=>ie.enterCallbacks={}),H=da(me,"beforeRouteEnter",A,q),H.push(ue),kn(H))).then(()=>{H=[];for(const ie of o.list())H.push(Bt(ie,A,q));return H.push(ue),kn(H)}).catch(ie=>kt(ie,8)?ie:Promise.reject(ie))}function T(A,q,H){for(const G of s.list())G(A,q,H)}function Y(A,q,H,G,se){const me=O(A,q);if(me)return me;const ue=q===Dt,ie=xn?history.state:{};H&&(G||ue?i.replace(A.fullPath,Ee({scroll:ue&&ie&&ie.scroll},se)):i.push(A.fullPath,se)),l.value=A,de(A,q,H,ue),J()}let j;function N(){j||(j=i.listen((A,q,H)=>{if(!Ye.listening)return;const G=h(A),se=I(G);if(se){w(Ee(se,{replace:!0}),G).catch(cr);return}c=G;const me=l.value;xn&&Cm(Ms(me.fullPath,H.delta),Ki()),x(G,me).catch(ue=>kt(ue,12)?ue:kt(ue,2)?(w(ue.to,G).then(ie=>{kt(ie,20)&&!H.delta&&H.type===Nn.pop&&i.go(-1,!1)}).catch(cr),Promise.reject()):(H.delta&&i.go(-H.delta,!1),L(ue,G,me))).then(ue=>{ue=ue||Y(G,me,!1),ue&&(H.delta&&!kt(ue,8)?i.go(-H.delta,!1):H.type===Nn.pop&&kt(ue,20)&&i.go(-1,!1)),T(G,me,ue)}).catch(cr)}))}let C=Jn(),D=Jn(),z;function L(A,q,H){J(A);const G=D.list();return G.length?G.forEach(se=>se(A,q,H)):console.error(A),Promise.reject(A)}function V(){return z&&l.value!==Dt?Promise.resolve():new Promise((A,q)=>{C.add([A,q])})}function J(A){return z||(z=!A,N(),C.list().forEach(([q,H])=>A?H(A):q()),C.reset()),A}function de(A,q,H,G){const{scrollBehavior:se}=e;if(!xn||!se)return Promise.resolve();const me=!H&&Om(Ms(A.fullPath,0))||(G||!H)&&history.state&&history.state.scroll||null;return Qt().then(()=>se(A,q,me)).then(ue=>ue&&Em(ue)).catch(ue=>L(ue,A,q))}const we=A=>i.go(A);let ve;const xe=new Set,Ye={currentRoute:l,listening:!0,addRoute:g,removeRoute:m,hasRoute:k,getRoutes:y,resolve:h,options:e,push:S,replace:$,go:we,back:()=>we(-1),forward:()=>we(1),beforeEach:a.add,beforeResolve:o.add,afterEach:s.add,onError:D.add,isReady:V,install(A){const q=this;A.component("RouterLink",Ou),A.component("RouterView",ph),A.config.globalProperties.$router=q,Object.defineProperty(A.config.globalProperties,"$route",{enumerable:!0,get:()=>ae(l)}),xn&&!ve&&l.value===Dt&&(ve=!0,S(i.location).catch(se=>{}));const H={};for(const se in Dt)H[se]=ce(()=>l.value[se]);A.provide(qi,q),A.provide(Co,Tr(H)),A.provide(Na,l);const G=A.unmount;xe.add(A),A.unmount=function(){xe.delete(A),xe.size<1&&(c=Dt,j&&j(),j=null,l.value=Dt,ve=!1,z=!1),G()}}};return Ye}function kn(e){return e.reduce((t,n)=>t.then(()=>n()),Promise.resolve())}function hh(e,t){const n=[],r=[],i=[],a=Math.max(t.matched.length,e.matched.length);for(let o=0;oRn(c,s))?r.push(s):n.push(s));const l=e.matched[o];l&&(t.matched.find(c=>Rn(c,l))||i.push(l))}return[n,r,i]}function Gi(){return ft(qi)}function Ir(){return ft(Co)}function Da(e,t={},n){for(const r in e){const i=e[r],a=n?`${n}:${r}`:r;typeof i=="object"&&i!==null?Da(i,t,a):typeof i=="function"&&(t[a]=i)}return t}const gh={run:e=>e()},vh=()=>gh,Su=typeof console.createTask<"u"?console.createTask:vh;function bh(e,t){const n=t.shift(),r=Su(n);return e.reduce((i,a)=>i.then(()=>r.run(()=>a(...t))),Promise.resolve())}function _h(e,t){const n=t.shift(),r=Su(n);return Promise.all(e.map(i=>r.run(()=>i(...t))))}function pa(e,t){for(const n of[...e])n(t)}class yh{constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(t,n,r={}){if(!t||typeof n!="function")return()=>{};const i=t;let a;for(;this._deprecatedHooks[t];)a=this._deprecatedHooks[t],t=a.to;if(a&&!r.allowDeprecated){let o=a.message;o||(o=`${i} hook has been deprecated`+(a.to?`, please use ${a.to}`:"")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(o)||(console.warn(o),this._deprecatedMessages.add(o))}if(!n.name)try{Object.defineProperty(n,"name",{get:()=>"_"+t.replace(/\W+/g,"_")+"_hook_cb",configurable:!0})}catch{}return this._hooks[t]=this._hooks[t]||[],this._hooks[t].push(n),()=>{n&&(this.removeHook(t,n),n=void 0)}}hookOnce(t,n){let r,i=(...a)=>(typeof r=="function"&&r(),r=void 0,i=void 0,n(...a));return r=this.hook(t,i),r}removeHook(t,n){if(this._hooks[t]){const r=this._hooks[t].indexOf(n);r!==-1&&this._hooks[t].splice(r,1),this._hooks[t].length===0&&delete this._hooks[t]}}deprecateHook(t,n){this._deprecatedHooks[t]=typeof n=="string"?{to:n}:n;const r=this._hooks[t]||[];delete this._hooks[t];for(const i of r)this.hook(t,i)}deprecateHooks(t){Object.assign(this._deprecatedHooks,t);for(const n in t)this.deprecateHook(n,t[n])}addHooks(t){const n=Da(t),r=Object.keys(n).map(i=>this.hook(i,n[i]));return()=>{for(const i of r.splice(0,r.length))i()}}removeHooks(t){const n=Da(t);for(const r in n)this.removeHook(r,n[r])}removeAllHooks(){for(const t in this._hooks)delete this._hooks[t]}callHook(t,...n){return n.unshift(t),this.callHookWith(bh,t,...n)}callHookParallel(t,...n){return n.unshift(t),this.callHookWith(_h,t,...n)}callHookWith(t,n,...r){const i=this._before||this._after?{name:n,args:r,context:{}}:void 0;this._before&&pa(this._before,i);const a=t(n in this._hooks?[...this._hooks[n]]:[],r);return a instanceof Promise?a.finally(()=>{this._after&&i&&pa(this._after,i)}):(this._after&&i&&pa(this._after,i),a)}beforeEach(t){return this._before=this._before||[],this._before.push(t),()=>{if(this._before!==void 0){const n=this._before.indexOf(t);n!==-1&&this._before.splice(n,1)}}}afterEach(t){return this._after=this._after||[],this._after.push(t),()=>{if(this._after!==void 0){const n=this._after.indexOf(t);n!==-1&&this._after.splice(n,1)}}}}function wh(){return new yh}function kh(e){return Array.isArray(e)?e:[e]}const xh=["title","titleTemplate","script","style","noscript"],ai=["base","meta","link","style","script","noscript"],Eh=["title","titleTemplate","templateParams","base","htmlAttrs","bodyAttrs","meta","link","style","script","noscript"],Ch=["base","title","titleTemplate","bodyAttrs","htmlAttrs","templateParams"],Au=["tagPosition","tagPriority","tagDuplicateStrategy","innerHTML","textContent","processTemplateParams"],Oh=typeof window<"u";function Tu(e){let t=9;for(let n=0;n>>9)+65536).toString(16).substring(1,8).toLowerCase()}function Ys(e){return e._h||Tu(e._d?e._d:`${e.tag}:${e.textContent||e.innerHTML||""}:${Object.entries(e.props).map(([t,n])=>`${t}:${String(n)}`).join(",")}`)}function Pu(e,t){const{props:n,tag:r}=e;if(Ch.includes(r))return r;if(r==="link"&&n.rel==="canonical")return"canonical";if(n.charset)return"charset";const i=["id"];r==="meta"&&i.push("name","property","http-equiv");for(const a of i)if(typeof n[a]<"u"){const o=String(n[a]);return t&&!t(o)?!1:`${r}:${a}:${o}`}return!1}function Ks(e,t){return e==null?t||null:typeof e=="function"?e(t):e}async function Sh(e,t,n){const r={tag:e,props:await Iu(typeof t=="object"&&typeof t!="function"&&!(t instanceof Promise)?{...t}:{[["script","noscript","style"].includes(e)?"innerHTML":"textContent"]:t},["templateParams","titleTemplate"].includes(e))};return Au.forEach(i=>{const a=typeof r.props[i]<"u"?r.props[i]:n[i];typeof a<"u"&&((!["innerHTML","textContent"].includes(i)||xh.includes(r.tag))&&(r[i]=a),delete r.props[i])}),r.props.body&&(r.tagPosition="bodyClose",delete r.props.body),r.props.children&&(r.innerHTML=r.props.children,delete r.props.children),r.tag==="script"&&(typeof r.innerHTML=="object"&&(r.innerHTML=JSON.stringify(r.innerHTML),r.props.type=r.props.type||"application/json"),r.innerHTML&&["application/ld+json","application/json"].includes(r.props.type)&&(r.innerHTML=r.innerHTML.replace(/({...r,props:{...r.props,content:i}})):r}function Ah(e){return typeof e=="object"&&!Array.isArray(e)&&(e=Object.keys(e).filter(t=>e[t])),(Array.isArray(e)?e.join(" "):e).split(" ").filter(t=>t.trim()).filter(Boolean).join(" ")}async function Iu(e,t){for(const n of Object.keys(e)){if(n==="class"){e[n]=Ah(e[n]);continue}if(e[n]instanceof Promise&&(e[n]=await e[n]),!t&&!Au.includes(n)){const r=String(e[n]),i=n.startsWith("data-");r==="true"||r===""?e[n]=i?"true":!0:e[n]||(i&&r==="false"?e[n]="false":delete e[n])}}return e}const Th=10;async function Ph(e){const t=[];return Object.entries(e.resolvedInput).filter(([n,r])=>typeof r<"u"&&Eh.includes(n)).forEach(([n,r])=>{const i=kh(r);t.push(...i.map(a=>Sh(n,a,e)).flat())}),(await Promise.all(t)).flat().filter(Boolean).map((n,r)=>(n._e=e._i,e.mode&&(n._m=e.mode),n._p=(e._i<l&&l[c]||void 0,t):s=t[o],typeof s<"u"?(s||"").replace(/"/g,'\\"'):!1}let i=e;try{i=decodeURI(e)}catch{}return(i.match(/%(\w+\.+\w+)|%(\w+)/g)||[]).sort().reverse().forEach(o=>{const s=r(o.slice(1));typeof s=="string"&&(e=e.replace(new RegExp(`\\${o}(\\W|$)`,"g"),(l,c)=>`${s}${c}`).trim())}),e.includes(jt)&&(e.endsWith(jt)&&(e=e.slice(0,-jt.length).trim()),e.startsWith(jt)&&(e=e.slice(jt.length).trim()),e=e.replace(new RegExp(`\\${jt}\\s*\\${jt}`,"g"),jt),e=on(e,{separator:n},n)),e}async function Mh(e){const t={tag:e.tagName.toLowerCase(),props:await Iu(e.getAttributeNames().reduce((n,r)=>({...n,[r]:e.getAttribute(r)}),{})),innerHTML:e.innerHTML};return t._d=Pu(t),t}async function $h(e,t={}){var u;const n=t.document||e.resolvedOptions.document;if(!n)return;const r={shouldRender:e.dirty,tags:[]};if(await e.hooks.callHook("dom:beforeRender",r),!r.shouldRender)return;const i=(await e.resolveTags()).map(f=>({tag:f,id:ai.includes(f.tag)?Ys(f):f.tag,shouldRender:!0}));let a=e._dom;if(!a){a={elMap:{htmlAttrs:n.documentElement,bodyAttrs:n.body}};for(const f of["body","head"]){const d=(u=n==null?void 0:n[f])==null?void 0:u.children;for(const g of[...d].filter(m=>ai.includes(m.tagName.toLowerCase())))a.elMap[g.getAttribute("data-hid")||Ys(await Mh(g))]=g}}a.pendingSideEffects={...a.sideEffects||{}},a.sideEffects={};function o(f,d,g){const m=`${f}:${d}`;a.sideEffects[m]=g,delete a.pendingSideEffects[m]}function s({id:f,$el:d,tag:g}){const m=g.tag.endsWith("Attrs");a.elMap[f]=d,m||(["textContent","innerHTML"].forEach(y=>{g[y]&&g[y]!==d[y]&&(d[y]=g[y])}),o(f,"el",()=>{a.elMap[f].remove(),delete a.elMap[f]})),Object.entries(g.props).forEach(([y,k])=>{const h=`attr:${y}`;if(y==="class")for(const v of(k||"").split(" ").filter(Boolean))m&&o(f,`${h}:${v}`,()=>d.classList.remove(v)),!d.classList.contains(v)&&d.classList.add(v);else d.getAttribute(y)!==k&&d.setAttribute(y,k===!0?"":String(k)),m&&o(f,h,()=>d.removeAttribute(y))})}const l=[],c={bodyClose:void 0,bodyOpen:void 0,head:void 0};for(const f of i){const{tag:d,shouldRender:g,id:m}=f;if(g){if(d.tag==="title"){n.title=d.textContent;continue}f.$el=f.$el||a.elMap[m],f.$el?s(f):ai.includes(d.tag)&&l.push(f)}}for(const f of l){const d=f.tag.tagPosition||"head";f.$el=n.createElement(f.tag.tag),s(f),c[d]=c[d]||n.createDocumentFragment(),c[d].appendChild(f.$el)}for(const f of i)await e.hooks.callHook("dom:renderTag",f,n,o);c.head&&n.head.appendChild(c.head),c.bodyOpen&&n.body.insertBefore(c.bodyOpen,n.body.firstChild),c.bodyClose&&n.body.appendChild(c.bodyClose),Object.values(a.pendingSideEffects).forEach(f=>f()),e._dom=a,e.dirty=!1,await e.hooks.callHook("dom:rendered",{renders:i})}async function Lh(e,t={}){const n=t.delayFn||(r=>setTimeout(r,10));return e._domUpdatePromise=e._domUpdatePromise||new Promise(r=>n(async()=>{await $h(e,t),delete e._domUpdatePromise,r()}))}function Rh(e){return t=>{var r,i;const n=((i=(r=t.resolvedOptions.document)==null?void 0:r.head.querySelector('script[id="unhead:payload"]'))==null?void 0:i.innerHTML)||!1;return n&&t.push(JSON.parse(n)),{mode:"client",hooks:{"entries:updated":function(a){Lh(a,e)}}}}}const Nh=["templateParams","htmlAttrs","bodyAttrs"],Dh={hooks:{"tag:normalise":function({tag:e}){["hid","vmid","key"].forEach(r=>{e.props[r]&&(e.key=e.props[r],delete e.props[r])});const n=Pu(e)||(e.key?`${e.tag}:${e.key}`:!1);n&&(e._d=n)},"tags:resolve":function(e){const t={};e.tags.forEach(r=>{const i=(r.key?`${r.tag}:${r.key}`:r._d)||r._p,a=t[i];if(a){let s=r==null?void 0:r.tagDuplicateStrategy;if(!s&&Nh.includes(r.tag)&&(s="merge"),s==="merge"){const l=a.props;["class","style"].forEach(c=>{r.props[c]&&l[c]&&(c==="style"&&!l[c].endsWith(";")&&(l[c]+=";"),r.props[c]=`${l[c]} ${r.props[c]}`)}),t[i].props={...l,...r.props};return}else if(r._e===a._e){a._duped=a._duped||[],r._d=`${a._d}:${a._duped.length+1}`,a._duped.push(r);return}else if(yi(r)>yi(a))return}const o=Object.keys(r.props).length+(r.innerHTML?1:0)+(r.textContent?1:0);if(ai.includes(r.tag)&&o===0){delete t[i];return}t[i]=r});const n=[];Object.values(t).forEach(r=>{const i=r._duped;delete r._duped,n.push(r),i&&n.push(...i)}),e.tags=n,e.tags=e.tags.filter(r=>!(r.tag==="meta"&&(r.props.name||r.props.property)&&!r.props.content))}}},jh={mode:"server",hooks:{"tags:resolve":function(e){const t={};e.tags.filter(n=>["titleTemplate","templateParams","title"].includes(n.tag)&&n._m==="server").forEach(n=>{t[n.tag]=n.tag.startsWith("title")?n.textContent:n.props}),Object.keys(t).length&&e.tags.push({tag:"script",innerHTML:JSON.stringify(t),props:{id:"unhead:payload",type:"application/json"}})}}},Qs=["script","link","bodyAttrs"];function Xs(e){const t={},n={};return Object.entries(e.props).forEach(([r,i])=>{r.startsWith("on")&&typeof i=="function"?n[r]=i:t[r]=i}),{props:t,eventHandlers:n}}const Fh={hooks:{"ssr:render":function(e){e.tags=e.tags.map(t=>(!Qs.includes(t.tag)||!Object.entries(t.props).find(([n,r])=>n.startsWith("on")&&typeof r=="function")||(t.props=Xs(t).props),t))},"tags:resolve":function(e){e.tags=e.tags.map(t=>{if(!Qs.includes(t.tag))return t;const{props:n,eventHandlers:r}=Xs(t);return Object.keys(r).length&&(t.props=n,t._eventHandlers=r),t})},"dom:renderTag":function(e,t,n){if(!e.tag._eventHandlers)return;const r=e.tag.tag==="bodyAttrs"?t.defaultView:e.$el;Object.entries(e.tag._eventHandlers).forEach(([i,a])=>{const o=`${e.tag._d||e.tag._p}:${i}`,s=i.slice(2).toLowerCase(),l=`data-h-${s}`;if(n(e.id,o,()=>{}),e.$el.hasAttribute(l))return;const c=a;e.$el.setAttribute(l,""),r.addEventListener(s,c),e.entry&&n(e.id,o,()=>{r.removeEventListener(s,c),e.$el.removeAttribute(l)})})}}},Hh=["link","style","script","noscript"],Bh={hooks:{"tag:normalise":({tag:e})=>{e.key&&Hh.includes(e.tag)&&(e.props["data-hid"]=e._h=Tu(e.key))}}},zh={hooks:{"tags:resolve":e=>{const t=n=>{var r;return(r=e.tags.find(i=>i._d===n))==null?void 0:r._p};for(const{prefix:n,offset:r}of Ih)for(const i of e.tags.filter(a=>typeof a.tagPriority=="string"&&a.tagPriority.startsWith(n))){const a=t(i.tagPriority.replace(n,""));typeof a<"u"&&(i._p=a+r)}e.tags.sort((n,r)=>n._p-r._p).sort((n,r)=>yi(n)-yi(r))}}},Uh={hooks:{"tags:resolve":e=>{var o;const{tags:t}=e,n=(o=t.find(s=>s.tag==="title"))==null?void 0:o.textContent,r=t.findIndex(s=>s.tag==="templateParams"),i=r!==-1?t[r].props:{},a=i.separator||"|";delete i.separator,i.pageTitle=on(i.pageTitle||n||"",i,a);for(const s of t)s.processTemplateParams!==!1&&(["titleTemplate","title"].includes(s.tag)&&typeof s.textContent=="string"?s.textContent=on(s.textContent,i,a):s.tag==="meta"&&typeof s.props.content=="string"?s.props.content=on(s.props.content,i,a):s.tag==="link"&&typeof s.props.href=="string"?s.props.href=on(s.props.href,i,a):s.processTemplateParams===!0&&(s.innerHTML?s.innerHTML=on(s.innerHTML,i,a):s.textContent&&(s.textContent=on(s.textContent,i,a))));e.tags=t.filter(s=>s.tag!=="templateParams")}}},Vh={hooks:{"tags:resolve":e=>{const{tags:t}=e;let n=t.findIndex(i=>i.tag==="titleTemplate");const r=t.findIndex(i=>i.tag==="title");if(r!==-1&&n!==-1){const i=Ks(t[n].textContent,t[r].textContent);i!==null?t[r].textContent=i||t[r].textContent:delete t[r]}else if(n!==-1){const i=Ks(t[n].textContent);i!==null&&(t[n].textContent=i,t[n].tag="title",n=-1)}n!==-1&&delete t[n],e.tags=t.filter(Boolean)}}};let Mu;function Wh(e={}){const t=Yh(e);return t.use(Rh()),Mu=t}function Js(e,t){return!e||e==="server"&&t||e==="client"&&!t}function Yh(e={}){const t=wh();t.addHooks(e.hooks||{}),e.document=e.document||(Oh?document:void 0);const n=!e.document;e.plugins=[Dh,jh,Fh,Bh,zh,Uh,Vh,...(e==null?void 0:e.plugins)||[]];const r=()=>{o.dirty=!0,t.callHook("entries:updated",o)};let i=0,a=[];const o={dirty:!1,resolvedOptions:e,hooks:t,headEntries(){return a},use(s){const l=typeof s=="function"?s(o):s;Js(l.mode,n)&&t.addHooks(l.hooks||{})},push(s,l){l==null||delete l.head;const c={_i:i++,input:s,...l};return Js(c.mode,n)&&(a.push(c),r()),{dispose(){a=a.filter(u=>u._i!==c._i),t.callHook("entries:updated",o),r()},patch(u){a=a.map(f=>(f._i===c._i&&(f.input=c.input=u),f)),r()}}},async resolveTags(){const s={tags:[],entries:[...a]};await t.callHook("entries:resolve",s);for(const l of s.entries){const c=l.resolvedInput||l.input;if(l.resolvedInput=await(l.transform?l.transform(c):c),l.resolvedInput)for(const u of await Ph(l)){const f={tag:u,entry:l,resolvedOptions:o.resolvedOptions};await t.callHook("tag:normalise",f),s.tags.push(f.tag)}}return await t.callHook("tags:beforeResolve",s),await t.callHook("tags:resolve",s),s.tags},ssr:n};return e.plugins.forEach(s=>o.use(s)),o.hooks.callHook("init",o),o}function Kh(){return Mu}const qh=lu.startsWith("3");function Gh(e){return typeof e=="function"?e():ae(e)}function wi(e,t=""){if(e instanceof Promise)return e;const n=Gh(e);return!e||!n?n:Array.isArray(n)?n.map(r=>wi(r,t)):typeof n=="object"?Object.fromEntries(Object.entries(n).map(([r,i])=>r==="titleTemplate"||r.startsWith("on")?[r,ae(i)]:[r,wi(i,r)])):n}const Qh={hooks:{"entries:resolve":function(e){for(const t of e.entries)t.resolvedInput=wi(t.input)}}},$u="usehead";function Xh(e){return{install(n){qh&&(n.config.globalProperties.$unhead=e,n.config.globalProperties.$head=e,n.provide($u,e))}}.install}function Jh(e={}){e.domDelayFn=e.domDelayFn||(n=>Qt(()=>setTimeout(()=>n(),0)));const t=Wh(e);return t.use(Qh),t.install=Xh(t),t}const Zs=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},el="__unhead_injection_handler__";function Zh(){if(el in Zs)return Zs[el]();const e=ft($u);return e||Kh()}function eg(e,t={}){const n=t.head||Zh();if(n)return n.ssr?n.push(e,t):tg(n,e,t)}function tg(e,t,n={}){const r=Ce(!1),i=Ce({});Nc(()=>{i.value=r.value?{}:wi(t)});const a=e.push(i.value,n);return je(i,s=>{a.patch(s)}),xo()&&(vo(()=>{a.dispose()}),zc(()=>{r.value=!0}),Bc(()=>{r.value=!1})),a}function ng(e){try{return JSON.parse(e||"{}")}catch(t){return console.error("[SSG] On state deserialization -",t,e),{}}}function rg(e){return document.readyState==="loading"?new Promise(t=>{document.addEventListener("DOMContentLoaded",()=>t(e))}):Promise.resolve(e)}const ig=he({setup(e,{slots:t}){const n=Ce(!1);return Xt(()=>n.value=!0),()=>n.value?t.default&&t.default({}):t.placeholder&&t.placeholder({})}});function ag(e,t,n,r={}){const{transformState:i,registerComponents:a=!0,useHead:o=!0,rootContainer:s="#app"}=r,l=typeof window<"u";async function c(u=!1,f){const d=u?pm(e):mm(e);let g;o&&(g=Jh(),d.use(g));const m=mh({history:u?Pm(t.base):Im(t.base),...t}),{routes:y}=t;a&&d.component("ClientOnly",ig);const k=[],O={app:d,head:g,isClient:l,router:m,routes:y,onSSRAppRendered:u?()=>{}:w=>k.push(w),triggerOnSSRAppRendered:()=>Promise.all(k.map(w=>w())),initialState:{},transformState:i,routePath:f};u&&(await rg(),O.initialState=(i==null?void 0:i(window.__INITIAL_STATE__||{}))||ng(window.__INITIAL_STATE__)),await(n==null?void 0:n(O)),d.use(m);let S,$=!0;if(m.beforeEach((w,_,x)=>{($||S&&S===w.path)&&($=!1,S=w.path,w.meta.state=O.initialState),x()}),!u){const w=O.routePath??"/";m.push(w),await m.isReady(),O.initialState=m.currentRoute.value.meta.state||{}}const I=O.initialState;return{...O,initialState:I}}return l&&(async()=>{const{app:u,router:f}=await c(!0);await f.isReady(),u.mount(s,!0)})(),c}const Mr=he({__name:"IsomorphicLink",props:{href:{default:void 0},target:{default:"_blank"},external:{type:Boolean}},setup(e){const t=e,n=ce(()=>typeof t.href=="string"&&/(^(mailto|http)|^\.$)/.test(t.href)||t.external),r=ce(()=>n.value?"a":Ou),i=ce(()=>n.value?{href:t.href,target:t.target,rel:"noreferrer noopener"}:{to:t.href});return(a,o)=>(te(),ke(ap(ae(r)),Kf(au(ae(i))),{default:_e(()=>[Zt(a.$slots,"default",{isExternal:ae(n)})]),_:3},16))}});function tl(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function K(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n-1;i--){var a=n[i],o=(a.tagName||"").toUpperCase();["STYLE","LINK"].indexOf(o)>-1&&(r=a)}return Me.head.insertBefore(t,r),e}}var Pg="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";function Or(){for(var e=12,t="";e-- >0;)t+=Pg[Math.random()*62|0];return t}function Vn(e){for(var t=[],n=(e||[]).length>>>0;n--;)t[n]=e[n];return t}function Io(e){return e.classList?Vn(e.classList):(e.getAttribute("class")||"").split(" ").filter(function(t){return t})}function Wu(e){return"".concat(e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function Ig(e){return Object.keys(e||{}).reduce(function(t,n){return t+"".concat(n,'="').concat(Wu(e[n]),'" ')},"").trim()}function Qi(e){return Object.keys(e||{}).reduce(function(t,n){return t+"".concat(n,": ").concat(e[n].trim(),";")},"")}function Mo(e){return e.size!==_t.size||e.x!==_t.x||e.y!==_t.y||e.rotate!==_t.rotate||e.flipX||e.flipY}function Mg(e){var t=e.transform,n=e.containerWidth,r=e.iconWidth,i={transform:"translate(".concat(n/2," 256)")},a="translate(".concat(t.x*32,", ").concat(t.y*32,") "),o="scale(".concat(t.size/16*(t.flipX?-1:1),", ").concat(t.size/16*(t.flipY?-1:1),") "),s="rotate(".concat(t.rotate," 0 0)"),l={transform:"".concat(a," ").concat(o," ").concat(s)},c={transform:"translate(".concat(r/2*-1," -256)")};return{outer:i,inner:l,path:c}}function $g(e){var t=e.transform,n=e.width,r=n===void 0?Fa:n,i=e.height,a=i===void 0?Fa:i,o=e.startCentered,s=o===void 0?!1:o,l="";return s&&ju?l+="translate(".concat(t.x/Ft-r/2,"em, ").concat(t.y/Ft-a/2,"em) "):s?l+="translate(calc(-50% + ".concat(t.x/Ft,"em), calc(-50% + ").concat(t.y/Ft,"em)) "):l+="translate(".concat(t.x/Ft,"em, ").concat(t.y/Ft,"em) "),l+="scale(".concat(t.size/Ft*(t.flipX?-1:1),", ").concat(t.size/Ft*(t.flipY?-1:1),") "),l+="rotate(".concat(t.rotate,"deg) "),l}var Lg=`:root, :host { + --fa-font-solid: normal 900 1em/1 "Font Awesome 6 Solid"; + --fa-font-regular: normal 400 1em/1 "Font Awesome 6 Regular"; + --fa-font-light: normal 300 1em/1 "Font Awesome 6 Light"; + --fa-font-thin: normal 100 1em/1 "Font Awesome 6 Thin"; + --fa-font-duotone: normal 900 1em/1 "Font Awesome 6 Duotone"; + --fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 6 Sharp"; + --fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"; +} + +svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa { + overflow: visible; + box-sizing: content-box; +} + +.svg-inline--fa { + display: var(--fa-display, inline-block); + height: 1em; + overflow: visible; + vertical-align: -0.125em; +} +.svg-inline--fa.fa-2xs { + vertical-align: 0.1em; +} +.svg-inline--fa.fa-xs { + vertical-align: 0em; +} +.svg-inline--fa.fa-sm { + vertical-align: -0.0714285705em; +} +.svg-inline--fa.fa-lg { + vertical-align: -0.2em; +} +.svg-inline--fa.fa-xl { + vertical-align: -0.25em; +} +.svg-inline--fa.fa-2xl { + vertical-align: -0.3125em; +} +.svg-inline--fa.fa-pull-left { + margin-right: var(--fa-pull-margin, 0.3em); + width: auto; +} +.svg-inline--fa.fa-pull-right { + margin-left: var(--fa-pull-margin, 0.3em); + width: auto; +} +.svg-inline--fa.fa-li { + width: var(--fa-li-width, 2em); + top: 0.25em; +} +.svg-inline--fa.fa-fw { + width: var(--fa-fw-width, 1.25em); +} + +.fa-layers svg.svg-inline--fa { + bottom: 0; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; +} + +.fa-layers-counter, .fa-layers-text { + display: inline-block; + position: absolute; + text-align: center; +} + +.fa-layers { + display: inline-block; + height: 1em; + position: relative; + text-align: center; + vertical-align: -0.125em; + width: 1em; +} +.fa-layers svg.svg-inline--fa { + -webkit-transform-origin: center center; + transform-origin: center center; +} + +.fa-layers-text { + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + -webkit-transform-origin: center center; + transform-origin: center center; +} + +.fa-layers-counter { + background-color: var(--fa-counter-background-color, #ff253a); + border-radius: var(--fa-counter-border-radius, 1em); + box-sizing: border-box; + color: var(--fa-inverse, #fff); + line-height: var(--fa-counter-line-height, 1); + max-width: var(--fa-counter-max-width, 5em); + min-width: var(--fa-counter-min-width, 1.5em); + overflow: hidden; + padding: var(--fa-counter-padding, 0.25em 0.5em); + right: var(--fa-right, 0); + text-overflow: ellipsis; + top: var(--fa-top, 0); + -webkit-transform: scale(var(--fa-counter-scale, 0.25)); + transform: scale(var(--fa-counter-scale, 0.25)); + -webkit-transform-origin: top right; + transform-origin: top right; +} + +.fa-layers-bottom-right { + bottom: var(--fa-bottom, 0); + right: var(--fa-right, 0); + top: auto; + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: bottom right; + transform-origin: bottom right; +} + +.fa-layers-bottom-left { + bottom: var(--fa-bottom, 0); + left: var(--fa-left, 0); + right: auto; + top: auto; + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: bottom left; + transform-origin: bottom left; +} + +.fa-layers-top-right { + top: var(--fa-top, 0); + right: var(--fa-right, 0); + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: top right; + transform-origin: top right; +} + +.fa-layers-top-left { + left: var(--fa-left, 0); + right: auto; + top: var(--fa-top, 0); + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: top left; + transform-origin: top left; +} + +.fa-1x { + font-size: 1em; +} + +.fa-2x { + font-size: 2em; +} + +.fa-3x { + font-size: 3em; +} + +.fa-4x { + font-size: 4em; +} + +.fa-5x { + font-size: 5em; +} + +.fa-6x { + font-size: 6em; +} + +.fa-7x { + font-size: 7em; +} + +.fa-8x { + font-size: 8em; +} + +.fa-9x { + font-size: 9em; +} + +.fa-10x { + font-size: 10em; +} + +.fa-2xs { + font-size: 0.625em; + line-height: 0.1em; + vertical-align: 0.225em; +} + +.fa-xs { + font-size: 0.75em; + line-height: 0.0833333337em; + vertical-align: 0.125em; +} + +.fa-sm { + font-size: 0.875em; + line-height: 0.0714285718em; + vertical-align: 0.0535714295em; +} + +.fa-lg { + font-size: 1.25em; + line-height: 0.05em; + vertical-align: -0.075em; +} + +.fa-xl { + font-size: 1.5em; + line-height: 0.0416666682em; + vertical-align: -0.125em; +} + +.fa-2xl { + font-size: 2em; + line-height: 0.03125em; + vertical-align: -0.1875em; +} + +.fa-fw { + text-align: center; + width: 1.25em; +} + +.fa-ul { + list-style-type: none; + margin-left: var(--fa-li-margin, 2.5em); + padding-left: 0; +} +.fa-ul > li { + position: relative; +} + +.fa-li { + left: calc(var(--fa-li-width, 2em) * -1); + position: absolute; + text-align: center; + width: var(--fa-li-width, 2em); + line-height: inherit; +} + +.fa-border { + border-color: var(--fa-border-color, #eee); + border-radius: var(--fa-border-radius, 0.1em); + border-style: var(--fa-border-style, solid); + border-width: var(--fa-border-width, 0.08em); + padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); +} + +.fa-pull-left { + float: left; + margin-right: var(--fa-pull-margin, 0.3em); +} + +.fa-pull-right { + float: right; + margin-left: var(--fa-pull-margin, 0.3em); +} + +.fa-beat { + -webkit-animation-name: fa-beat; + animation-name: fa-beat; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); +} + +.fa-bounce { + -webkit-animation-name: fa-bounce; + animation-name: fa-bounce; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); +} + +.fa-fade { + -webkit-animation-name: fa-fade; + animation-name: fa-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); +} + +.fa-beat-fade { + -webkit-animation-name: fa-beat-fade; + animation-name: fa-beat-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); +} + +.fa-flip { + -webkit-animation-name: fa-flip; + animation-name: fa-flip; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); +} + +.fa-shake { + -webkit-animation-name: fa-shake; + animation-name: fa-shake; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); +} + +.fa-spin { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 2s); + animation-duration: var(--fa-animation-duration, 2s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); +} + +.fa-spin-reverse { + --fa-animation-direction: reverse; +} + +.fa-pulse, +.fa-spin-pulse { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); + animation-timing-function: var(--fa-animation-timing, steps(8)); +} + +@media (prefers-reduced-motion: reduce) { + .fa-beat, +.fa-bounce, +.fa-fade, +.fa-beat-fade, +.fa-flip, +.fa-pulse, +.fa-shake, +.fa-spin, +.fa-spin-pulse { + -webkit-animation-delay: -1ms; + animation-delay: -1ms; + -webkit-animation-duration: 1ms; + animation-duration: 1ms; + -webkit-animation-iteration-count: 1; + animation-iteration-count: 1; + transition-delay: 0s; + transition-duration: 0s; + } +} +@-webkit-keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); + } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); + } +} +@keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); + } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); + } +} +@-webkit-keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } +} +@keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } +} +@-webkit-keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); + } +} +@keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); + } +} +@-webkit-keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); + } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); + } +} +@keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); + } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); + } +} +@-webkit-keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + } +} +@keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + } +} +@-webkit-keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); + } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); + } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); + } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); + } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); + } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); + } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); + } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); + } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); + } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); + } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); + } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); + } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); + } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); + } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); + } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); + } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +.fa-rotate-90 { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.fa-rotate-180 { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); +} + +.fa-rotate-270 { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); +} + +.fa-flip-horizontal { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); +} + +.fa-flip-vertical { + -webkit-transform: scale(1, -1); + transform: scale(1, -1); +} + +.fa-flip-both, +.fa-flip-horizontal.fa-flip-vertical { + -webkit-transform: scale(-1, -1); + transform: scale(-1, -1); +} + +.fa-rotate-by { + -webkit-transform: rotate(var(--fa-rotate-angle, none)); + transform: rotate(var(--fa-rotate-angle, none)); +} + +.fa-stack { + display: inline-block; + vertical-align: middle; + height: 2em; + position: relative; + width: 2.5em; +} + +.fa-stack-1x, +.fa-stack-2x { + bottom: 0; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; + z-index: var(--fa-stack-z-index, auto); +} + +.svg-inline--fa.fa-stack-1x { + height: 1em; + width: 1.25em; +} +.svg-inline--fa.fa-stack-2x { + height: 2em; + width: 2.5em; +} + +.fa-inverse { + color: var(--fa-inverse, #fff); +} + +.sr-only, +.fa-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; +} + +.sr-only-focusable:not(:focus), +.fa-sr-only-focusable:not(:focus) { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.svg-inline--fa .fa-primary { + fill: var(--fa-primary-color, currentColor); + opacity: var(--fa-primary-opacity, 1); +} + +.svg-inline--fa .fa-secondary { + fill: var(--fa-secondary-color, currentColor); + opacity: var(--fa-secondary-opacity, 0.4); +} + +.svg-inline--fa.fa-swap-opacity .fa-primary { + opacity: var(--fa-secondary-opacity, 0.4); +} + +.svg-inline--fa.fa-swap-opacity .fa-secondary { + opacity: var(--fa-primary-opacity, 1); +} + +.svg-inline--fa mask .fa-primary, +.svg-inline--fa mask .fa-secondary { + fill: black; +} + +.fad.fa-inverse, +.fa-duotone.fa-inverse { + color: var(--fa-inverse, #fff); +}`;function Yu(){var e=Fu,t=Hu,n=X.cssPrefix,r=X.replacementClass,i=Lg;if(n!==e||r!==t){var a=new RegExp("\\.".concat(e,"\\-"),"g"),o=new RegExp("\\--".concat(e,"\\-"),"g"),s=new RegExp("\\.".concat(t),"g");i=i.replace(a,".".concat(n,"-")).replace(o,"--".concat(n,"-")).replace(s,".".concat(r))}return i}var ll=!1;function ma(){X.autoAddCss&&!ll&&(Tg(Yu()),ll=!0)}var Rg={mixout:function(){return{dom:{css:Yu,insertCss:ma}}},hooks:function(){return{beforeDOMElementCreation:function(){ma()},beforeI2svg:function(){ma()}}}},At=Kt||{};At[St]||(At[St]={});At[St].styles||(At[St].styles={});At[St].hooks||(At[St].hooks={});At[St].shims||(At[St].shims=[]);var ct=At[St],Ku=[],Ng=function e(){Me.removeEventListener("DOMContentLoaded",e),xi=1,Ku.map(function(t){return t()})},xi=!1;Mt&&(xi=(Me.documentElement.doScroll?/^loaded|^c/:/^loaded|^i|^c/).test(Me.readyState),xi||Me.addEventListener("DOMContentLoaded",Ng));function Dg(e){Mt&&(xi?setTimeout(e,0):Ku.push(e))}function Rr(e){var t=e.tag,n=e.attributes,r=n===void 0?{}:n,i=e.children,a=i===void 0?[]:i;return typeof e=="string"?Wu(e):"<".concat(t," ").concat(Ig(r),">").concat(a.map(Rr).join(""),"")}function cl(e,t,n){if(e&&e[t]&&e[t][n])return{prefix:t,iconName:n,icon:e[t][n]}}var jg=function(t,n){return function(r,i,a,o){return t.call(n,r,i,a,o)}},ha=function(t,n,r,i){var a=Object.keys(t),o=a.length,s=i!==void 0?jg(n,i):n,l,c,u;for(r===void 0?(l=1,u=t[a[0]]):(l=0,u=r);l=55296&&i<=56319&&n=55296&&r<=56319&&n>t+1&&(i=e.charCodeAt(t+1),i>=56320&&i<=57343)?(r-55296)*1024+i-56320+65536:r}function ul(e){return Object.keys(e).reduce(function(t,n){var r=e[n],i=!!r.icon;return i?t[r.iconName]=r.icon:t[n]=r,t},{})}function za(e,t){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},r=n.skipHooks,i=r===void 0?!1:r,a=ul(t);typeof ct.hooks.addPack=="function"&&!i?ct.hooks.addPack(e,ul(t)):ct.styles[e]=K(K({},ct.styles[e]||{}),a),e==="fas"&&za("fa",t)}var Xr,Jr,Zr,En=ct.styles,Bg=ct.shims,zg=(Xr={},Fe(Xr,Ie,Object.values(Er[Ie])),Fe(Xr,Ne,Object.values(Er[Ne])),Xr),$o=null,qu={},Gu={},Qu={},Xu={},Ju={},Ug=(Jr={},Fe(Jr,Ie,Object.keys(kr[Ie])),Fe(Jr,Ne,Object.keys(kr[Ne])),Jr);function Vg(e){return~Eg.indexOf(e)}function Wg(e,t){var n=t.split("-"),r=n[0],i=n.slice(1).join("-");return r===e&&i!==""&&!Vg(i)?i:null}var Zu=function(){var t=function(a){return ha(En,function(o,s,l){return o[l]=ha(s,a,{}),o},{})};qu=t(function(i,a,o){if(a[3]&&(i[a[3]]=o),a[2]){var s=a[2].filter(function(l){return typeof l=="number"});s.forEach(function(l){i[l.toString(16)]=o})}return i}),Gu=t(function(i,a,o){if(i[o]=o,a[2]){var s=a[2].filter(function(l){return typeof l=="string"});s.forEach(function(l){i[l]=o})}return i}),Ju=t(function(i,a,o){var s=a[2];return i[o]=o,s.forEach(function(l){i[l]=o}),i});var n="far"in En||X.autoFetchSvg,r=ha(Bg,function(i,a){var o=a[0],s=a[1],l=a[2];return s==="far"&&!n&&(s="fas"),typeof o=="string"&&(i.names[o]={prefix:s,iconName:l}),typeof o=="number"&&(i.unicodes[o.toString(16)]={prefix:s,iconName:l}),i},{names:{},unicodes:{}});Qu=r.names,Xu=r.unicodes,$o=Xi(X.styleDefault,{family:X.familyDefault})};Ag(function(e){$o=Xi(e.styleDefault,{family:X.familyDefault})});Zu();function Lo(e,t){return(qu[e]||{})[t]}function Yg(e,t){return(Gu[e]||{})[t]}function pn(e,t){return(Ju[e]||{})[t]}function ef(e){return Qu[e]||{prefix:null,iconName:null}}function Kg(e){var t=Xu[e],n=Lo("fas",e);return t||(n?{prefix:"fas",iconName:n}:null)||{prefix:null,iconName:null}}function qt(){return $o}var Ro=function(){return{prefix:null,iconName:null,rest:[]}};function Xi(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},n=t.family,r=n===void 0?Ie:n,i=kr[r][e],a=xr[r][e]||xr[r][i],o=e in ct.styles?e:null;return a||o||null}var fl=(Zr={},Fe(Zr,Ie,Object.keys(Er[Ie])),Fe(Zr,Ne,Object.keys(Er[Ne])),Zr);function Ji(e){var t,n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=n.skipLookups,i=r===void 0?!1:r,a=(t={},Fe(t,Ie,"".concat(X.cssPrefix,"-").concat(Ie)),Fe(t,Ne,"".concat(X.cssPrefix,"-").concat(Ne)),t),o=null,s=Ie;(e.includes(a[Ie])||e.some(function(c){return fl[Ie].includes(c)}))&&(s=Ie),(e.includes(a[Ne])||e.some(function(c){return fl[Ne].includes(c)}))&&(s=Ne);var l=e.reduce(function(c,u){var f=Wg(X.cssPrefix,u);if(En[u]?(u=zg[s].includes(u)?bg[s][u]:u,o=u,c.prefix=u):Ug[s].indexOf(u)>-1?(o=u,c.prefix=Xi(u,{family:s})):f?c.iconName=f:u!==X.replacementClass&&u!==a[Ie]&&u!==a[Ne]&&c.rest.push(u),!i&&c.prefix&&c.iconName){var d=o==="fa"?ef(c.iconName):{},g=pn(c.prefix,c.iconName);d.prefix&&(o=null),c.iconName=d.iconName||g||c.iconName,c.prefix=d.prefix||c.prefix,c.prefix==="far"&&!En.far&&En.fas&&!X.autoFetchSvg&&(c.prefix="fas")}return c},Ro());return(e.includes("fa-brands")||e.includes("fab"))&&(l.prefix="fab"),(e.includes("fa-duotone")||e.includes("fad"))&&(l.prefix="fad"),!l.prefix&&s===Ne&&(En.fass||X.autoFetchSvg)&&(l.prefix="fass",l.iconName=pn(l.prefix,l.iconName)||l.iconName),(l.prefix==="fa"||o==="fa")&&(l.prefix=qt()||"fas"),l}var qg=function(){function e(){og(this,e),this.definitions={}}return sg(e,[{key:"add",value:function(){for(var n=this,r=arguments.length,i=new Array(r),a=0;a0&&u.forEach(function(f){typeof f=="string"&&(n[s][f]=c)}),n[s][l]=c}),n}}]),e}(),dl=[],Cn={},In={},Gg=Object.keys(In);function Qg(e,t){var n=t.mixoutsTo;return dl=e,Cn={},Object.keys(In).forEach(function(r){Gg.indexOf(r)===-1&&delete In[r]}),dl.forEach(function(r){var i=r.mixout?r.mixout():{};if(Object.keys(i).forEach(function(o){typeof i[o]=="function"&&(n[o]=i[o]),ki(i[o])==="object"&&Object.keys(i[o]).forEach(function(s){n[o]||(n[o]={}),n[o][s]=i[o][s]})}),r.hooks){var a=r.hooks();Object.keys(a).forEach(function(o){Cn[o]||(Cn[o]=[]),Cn[o].push(a[o])})}r.provides&&r.provides(In)}),n}function Ua(e,t){for(var n=arguments.length,r=new Array(n>2?n-2:0),i=2;i1?t-1:0),r=1;r0&&arguments[0]!==void 0?arguments[0]:{};return Mt?(bn("beforeI2svg",t),Tt("pseudoElements2svg",t),Tt("i2svg",t)):Promise.reject("Operation requires a DOM of some kind.")},watch:function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},n=t.autoReplaceSvgRoot;X.autoReplaceSvg===!1&&(X.autoReplaceSvg=!0),X.observeMutations=!0,Dg(function(){ev({autoReplaceSvgRoot:n}),bn("watch",t)})}},Zg={icon:function(t){if(t===null)return null;if(ki(t)==="object"&&t.prefix&&t.iconName)return{prefix:t.prefix,iconName:pn(t.prefix,t.iconName)||t.iconName};if(Array.isArray(t)&&t.length===2){var n=t[1].indexOf("fa-")===0?t[1].slice(3):t[1],r=Xi(t[0]);return{prefix:r,iconName:pn(r,n)||n}}if(typeof t=="string"&&(t.indexOf("".concat(X.cssPrefix,"-"))>-1||t.match(_g))){var i=Ji(t.split(" "),{skipLookups:!0});return{prefix:i.prefix||qt(),iconName:pn(i.prefix,i.iconName)||i.iconName}}if(typeof t=="string"){var a=qt();return{prefix:a,iconName:pn(a,t)||t}}}},et={noAuto:Xg,config:X,dom:Jg,parse:Zg,library:tf,findIconDefinition:Va,toHtml:Rr},ev=function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},n=t.autoReplaceSvgRoot,r=n===void 0?Me:n;(Object.keys(ct.styles).length>0||X.autoFetchSvg)&&Mt&&X.autoReplaceSvg&&et.dom.i2svg({node:r})};function Zi(e,t){return Object.defineProperty(e,"abstract",{get:t}),Object.defineProperty(e,"html",{get:function(){return e.abstract.map(function(r){return Rr(r)})}}),Object.defineProperty(e,"node",{get:function(){if(Mt){var r=Me.createElement("div");return r.innerHTML=e.html,r.children}}}),e}function tv(e){var t=e.children,n=e.main,r=e.mask,i=e.attributes,a=e.styles,o=e.transform;if(Mo(o)&&n.found&&!r.found){var s=n.width,l=n.height,c={x:s/l/2,y:.5};i.style=Qi(K(K({},a),{},{"transform-origin":"".concat(c.x+o.x/16,"em ").concat(c.y+o.y/16,"em")}))}return[{tag:"svg",attributes:i,children:t}]}function nv(e){var t=e.prefix,n=e.iconName,r=e.children,i=e.attributes,a=e.symbol,o=a===!0?"".concat(t,"-").concat(X.cssPrefix,"-").concat(n):a;return[{tag:"svg",attributes:{style:"display: none;"},children:[{tag:"symbol",attributes:K(K({},i),{},{id:o}),children:r}]}]}function No(e){var t=e.icons,n=t.main,r=t.mask,i=e.prefix,a=e.iconName,o=e.transform,s=e.symbol,l=e.title,c=e.maskId,u=e.titleId,f=e.extra,d=e.watchable,g=d===void 0?!1:d,m=r.found?r:n,y=m.width,k=m.height,h=i==="fak",v=[X.replacementClass,a?"".concat(X.cssPrefix,"-").concat(a):""].filter(function(x){return f.classes.indexOf(x)===-1}).filter(function(x){return x!==""||!!x}).concat(f.classes).join(" "),O={children:[],attributes:K(K({},f.attributes),{},{"data-prefix":i,"data-icon":a,class:v,role:f.attributes.role||"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 ".concat(y," ").concat(k)})},S=h&&!~f.classes.indexOf("fa-fw")?{width:"".concat(y/k*16*.0625,"em")}:{};g&&(O.attributes[vn]=""),l&&(O.children.push({tag:"title",attributes:{id:O.attributes["aria-labelledby"]||"title-".concat(u||Or())},children:[l]}),delete O.attributes.title);var $=K(K({},O),{},{prefix:i,iconName:a,main:n,mask:r,maskId:c,transform:o,symbol:s,styles:K(K({},S),f.styles)}),I=r.found&&n.found?Tt("generateAbstractMask",$)||{children:[],attributes:{}}:Tt("generateAbstractIcon",$)||{children:[],attributes:{}},w=I.children,_=I.attributes;return $.children=w,$.attributes=_,s?nv($):tv($)}function pl(e){var t=e.content,n=e.width,r=e.height,i=e.transform,a=e.title,o=e.extra,s=e.watchable,l=s===void 0?!1:s,c=K(K(K({},o.attributes),a?{title:a}:{}),{},{class:o.classes.join(" ")});l&&(c[vn]="");var u=K({},o.styles);Mo(i)&&(u.transform=$g({transform:i,startCentered:!0,width:n,height:r}),u["-webkit-transform"]=u.transform);var f=Qi(u);f.length>0&&(c.style=f);var d=[];return d.push({tag:"span",attributes:c,children:[t]}),a&&d.push({tag:"span",attributes:{class:"sr-only"},children:[a]}),d}function rv(e){var t=e.content,n=e.title,r=e.extra,i=K(K(K({},r.attributes),n?{title:n}:{}),{},{class:r.classes.join(" ")}),a=Qi(r.styles);a.length>0&&(i.style=a);var o=[];return o.push({tag:"span",attributes:i,children:[t]}),n&&o.push({tag:"span",attributes:{class:"sr-only"},children:[n]}),o}var ga=ct.styles;function Wa(e){var t=e[0],n=e[1],r=e.slice(4),i=Oo(r,1),a=i[0],o=null;return Array.isArray(a)?o={tag:"g",attributes:{class:"".concat(X.cssPrefix,"-").concat(dn.GROUP)},children:[{tag:"path",attributes:{class:"".concat(X.cssPrefix,"-").concat(dn.SECONDARY),fill:"currentColor",d:a[0]}},{tag:"path",attributes:{class:"".concat(X.cssPrefix,"-").concat(dn.PRIMARY),fill:"currentColor",d:a[1]}}]}:o={tag:"path",attributes:{fill:"currentColor",d:a}},{found:!0,width:t,height:n,icon:o}}var iv={found:!1,width:512,height:512};function av(e,t){!Bu&&!X.showMissingIcons&&e&&console.error('Icon with name "'.concat(e,'" and prefix "').concat(t,'" is missing.'))}function Ya(e,t){var n=t;return t==="fa"&&X.styleDefault!==null&&(t=qt()),new Promise(function(r,i){if(Tt("missingIconAbstract"),n==="fa"){var a=ef(e)||{};e=a.iconName||e,t=a.prefix||t}if(e&&t&&ga[t]&&ga[t][e]){var o=ga[t][e];return r(Wa(o))}av(e,t),r(K(K({},iv),{},{icon:X.showMissingIcons&&e?Tt("missingIconAbstract")||{}:{}}))})}var ml=function(){},Ka=X.measurePerformance&&Wr&&Wr.mark&&Wr.measure?Wr:{mark:ml,measure:ml},nr='FA "6.2.0"',ov=function(t){return Ka.mark("".concat(nr," ").concat(t," begins")),function(){return nf(t)}},nf=function(t){Ka.mark("".concat(nr," ").concat(t," ends")),Ka.measure("".concat(nr," ").concat(t),"".concat(nr," ").concat(t," begins"),"".concat(nr," ").concat(t," ends"))},Do={begin:ov,end:nf},oi=function(){};function hl(e){var t=e.getAttribute?e.getAttribute(vn):null;return typeof t=="string"}function sv(e){var t=e.getAttribute?e.getAttribute(Ao):null,n=e.getAttribute?e.getAttribute(To):null;return t&&n}function lv(e){return e&&e.classList&&e.classList.contains&&e.classList.contains(X.replacementClass)}function cv(){if(X.autoReplaceSvg===!0)return si.replace;var e=si[X.autoReplaceSvg];return e||si.replace}function uv(e){return Me.createElementNS("http://www.w3.org/2000/svg",e)}function fv(e){return Me.createElement(e)}function rf(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},n=t.ceFn,r=n===void 0?e.tag==="svg"?uv:fv:n;if(typeof e=="string")return Me.createTextNode(e);var i=r(e.tag);Object.keys(e.attributes||[]).forEach(function(o){i.setAttribute(o,e.attributes[o])});var a=e.children||[];return a.forEach(function(o){i.appendChild(rf(o,{ceFn:r}))}),i}function dv(e){var t=" ".concat(e.outerHTML," ");return t="".concat(t,"Font Awesome fontawesome.com "),t}var si={replace:function(t){var n=t[0];if(n.parentNode)if(t[1].forEach(function(i){n.parentNode.insertBefore(rf(i),n)}),n.getAttribute(vn)===null&&X.keepOriginalSource){var r=Me.createComment(dv(n));n.parentNode.replaceChild(r,n)}else n.remove()},nest:function(t){var n=t[0],r=t[1];if(~Io(n).indexOf(X.replacementClass))return si.replace(t);var i=new RegExp("".concat(X.cssPrefix,"-.*"));if(delete r[0].attributes.id,r[0].attributes.class){var a=r[0].attributes.class.split(" ").reduce(function(s,l){return l===X.replacementClass||l.match(i)?s.toSvg.push(l):s.toNode.push(l),s},{toNode:[],toSvg:[]});r[0].attributes.class=a.toSvg.join(" "),a.toNode.length===0?n.removeAttribute("class"):n.setAttribute("class",a.toNode.join(" "))}var o=r.map(function(s){return Rr(s)}).join(` +`);n.setAttribute(vn,""),n.innerHTML=o}};function gl(e){e()}function af(e,t){var n=typeof t=="function"?t:oi;if(e.length===0)n();else{var r=gl;X.mutateApproach===gg&&(r=Kt.requestAnimationFrame||gl),r(function(){var i=cv(),a=Do.begin("mutate");e.map(i),a(),n()})}}var jo=!1;function of(){jo=!0}function qa(){jo=!1}var Ei=null;function vl(e){if(ol&&X.observeMutations){var t=e.treeCallback,n=t===void 0?oi:t,r=e.nodeCallback,i=r===void 0?oi:r,a=e.pseudoElementsCallback,o=a===void 0?oi:a,s=e.observeMutationsRoot,l=s===void 0?Me:s;Ei=new ol(function(c){if(!jo){var u=qt();Vn(c).forEach(function(f){if(f.type==="childList"&&f.addedNodes.length>0&&!hl(f.addedNodes[0])&&(X.searchPseudoElements&&o(f.target),n(f.target)),f.type==="attributes"&&f.target.parentNode&&X.searchPseudoElements&&o(f.target.parentNode),f.type==="attributes"&&hl(f.target)&&~xg.indexOf(f.attributeName))if(f.attributeName==="class"&&sv(f.target)){var d=Ji(Io(f.target)),g=d.prefix,m=d.iconName;f.target.setAttribute(Ao,g||u),m&&f.target.setAttribute(To,m)}else lv(f.target)&&i(f.target)})}}),Mt&&Ei.observe(l,{childList:!0,attributes:!0,characterData:!0,subtree:!0})}}function pv(){Ei&&Ei.disconnect()}function mv(e){var t=e.getAttribute("style"),n=[];return t&&(n=t.split(";").reduce(function(r,i){var a=i.split(":"),o=a[0],s=a.slice(1);return o&&s.length>0&&(r[o]=s.join(":").trim()),r},{})),n}function hv(e){var t=e.getAttribute("data-prefix"),n=e.getAttribute("data-icon"),r=e.innerText!==void 0?e.innerText.trim():"",i=Ji(Io(e));return i.prefix||(i.prefix=qt()),t&&n&&(i.prefix=t,i.iconName=n),i.iconName&&i.prefix||(i.prefix&&r.length>0&&(i.iconName=Yg(i.prefix,e.innerText)||Lo(i.prefix,Ba(e.innerText))),!i.iconName&&X.autoFetchSvg&&e.firstChild&&e.firstChild.nodeType===Node.TEXT_NODE&&(i.iconName=e.firstChild.data)),i}function gv(e){var t=Vn(e.attributes).reduce(function(i,a){return i.name!=="class"&&i.name!=="style"&&(i[a.name]=a.value),i},{}),n=e.getAttribute("title"),r=e.getAttribute("data-fa-title-id");return X.autoA11y&&(n?t["aria-labelledby"]="".concat(X.replacementClass,"-title-").concat(r||Or()):(t["aria-hidden"]="true",t.focusable="false")),t}function vv(){return{iconName:null,title:null,titleId:null,prefix:null,transform:_t,symbol:!1,mask:{iconName:null,prefix:null,rest:[]},maskId:null,extra:{classes:[],styles:{},attributes:{}}}}function bl(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{styleParser:!0},n=hv(e),r=n.iconName,i=n.prefix,a=n.rest,o=gv(e),s=Ua("parseNodeAttributes",{},e),l=t.styleParser?mv(e):[];return K({iconName:r,title:e.getAttribute("title"),titleId:e.getAttribute("data-fa-title-id"),prefix:i,transform:_t,mask:{iconName:null,prefix:null,rest:[]},maskId:null,symbol:!1,extra:{classes:a,styles:l,attributes:o}},s)}var bv=ct.styles;function sf(e){var t=X.autoReplaceSvg==="nest"?bl(e,{styleParser:!1}):bl(e);return~t.extra.classes.indexOf(zu)?Tt("generateLayersText",e,t):Tt("generateSvgReplacementMutation",e,t)}var Gt=new Set;Po.map(function(e){Gt.add("fa-".concat(e))});Object.keys(kr[Ie]).map(Gt.add.bind(Gt));Object.keys(kr[Ne]).map(Gt.add.bind(Gt));Gt=$r(Gt);function _l(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;if(!Mt)return Promise.resolve();var n=Me.documentElement.classList,r=function(f){return n.add("".concat(sl,"-").concat(f))},i=function(f){return n.remove("".concat(sl,"-").concat(f))},a=X.autoFetchSvg?Gt:Po.map(function(u){return"fa-".concat(u)}).concat(Object.keys(bv));a.includes("fa")||a.push("fa");var o=[".".concat(zu,":not([").concat(vn,"])")].concat(a.map(function(u){return".".concat(u,":not([").concat(vn,"])")})).join(", ");if(o.length===0)return Promise.resolve();var s=[];try{s=Vn(e.querySelectorAll(o))}catch{}if(s.length>0)r("pending"),i("complete");else return Promise.resolve();var l=Do.begin("onTree"),c=s.reduce(function(u,f){try{var d=sf(f);d&&u.push(d)}catch(g){Bu||g.name==="MissingIcon"&&console.error(g)}return u},[]);return new Promise(function(u,f){Promise.all(c).then(function(d){af(d,function(){r("active"),r("complete"),i("pending"),typeof t=="function"&&t(),l(),u()})}).catch(function(d){l(),f(d)})})}function _v(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;sf(e).then(function(n){n&&af([n],t)})}function yv(e){return function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=(t||{}).icon?t:Va(t||{}),i=n.mask;return i&&(i=(i||{}).icon?i:Va(i||{})),e(r,K(K({},n),{},{mask:i}))}}var wv=function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=n.transform,i=r===void 0?_t:r,a=n.symbol,o=a===void 0?!1:a,s=n.mask,l=s===void 0?null:s,c=n.maskId,u=c===void 0?null:c,f=n.title,d=f===void 0?null:f,g=n.titleId,m=g===void 0?null:g,y=n.classes,k=y===void 0?[]:y,h=n.attributes,v=h===void 0?{}:h,O=n.styles,S=O===void 0?{}:O;if(t){var $=t.prefix,I=t.iconName,w=t.icon;return Zi(K({type:"icon"},t),function(){return bn("beforeDOMElementCreation",{iconDefinition:t,params:n}),X.autoA11y&&(d?v["aria-labelledby"]="".concat(X.replacementClass,"-title-").concat(m||Or()):(v["aria-hidden"]="true",v.focusable="false")),No({icons:{main:Wa(w),mask:l?Wa(l.icon):{found:!1,width:null,height:null,icon:{}}},prefix:$,iconName:I,transform:K(K({},_t),i),symbol:o,title:d,maskId:u,titleId:m,extra:{attributes:v,styles:S,classes:k}})})}},kv={mixout:function(){return{icon:yv(wv)}},hooks:function(){return{mutationObserverCallbacks:function(n){return n.treeCallback=_l,n.nodeCallback=_v,n}}},provides:function(t){t.i2svg=function(n){var r=n.node,i=r===void 0?Me:r,a=n.callback,o=a===void 0?function(){}:a;return _l(i,o)},t.generateSvgReplacementMutation=function(n,r){var i=r.iconName,a=r.title,o=r.titleId,s=r.prefix,l=r.transform,c=r.symbol,u=r.mask,f=r.maskId,d=r.extra;return new Promise(function(g,m){Promise.all([Ya(i,s),u.iconName?Ya(u.iconName,u.prefix):Promise.resolve({found:!1,width:512,height:512,icon:{}})]).then(function(y){var k=Oo(y,2),h=k[0],v=k[1];g([n,No({icons:{main:h,mask:v},prefix:s,iconName:i,transform:l,symbol:c,maskId:f,title:a,titleId:o,extra:d,watchable:!0})])}).catch(m)})},t.generateAbstractIcon=function(n){var r=n.children,i=n.attributes,a=n.main,o=n.transform,s=n.styles,l=Qi(s);l.length>0&&(i.style=l);var c;return Mo(o)&&(c=Tt("generateAbstractTransformGrouping",{main:a,transform:o,containerWidth:a.width,iconWidth:a.width})),r.push(c||a.icon),{children:r,attributes:i}}}},xv={mixout:function(){return{layer:function(n){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.classes,a=i===void 0?[]:i;return Zi({type:"layer"},function(){bn("beforeDOMElementCreation",{assembler:n,params:r});var o=[];return n(function(s){Array.isArray(s)?s.map(function(l){o=o.concat(l.abstract)}):o=o.concat(s.abstract)}),[{tag:"span",attributes:{class:["".concat(X.cssPrefix,"-layers")].concat($r(a)).join(" ")},children:o}]})}}}},Ev={mixout:function(){return{counter:function(n){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.title,a=i===void 0?null:i,o=r.classes,s=o===void 0?[]:o,l=r.attributes,c=l===void 0?{}:l,u=r.styles,f=u===void 0?{}:u;return Zi({type:"counter",content:n},function(){return bn("beforeDOMElementCreation",{content:n,params:r}),rv({content:n.toString(),title:a,extra:{attributes:c,styles:f,classes:["".concat(X.cssPrefix,"-layers-counter")].concat($r(s))}})})}}}},Cv={mixout:function(){return{text:function(n){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.transform,a=i===void 0?_t:i,o=r.title,s=o===void 0?null:o,l=r.classes,c=l===void 0?[]:l,u=r.attributes,f=u===void 0?{}:u,d=r.styles,g=d===void 0?{}:d;return Zi({type:"text",content:n},function(){return bn("beforeDOMElementCreation",{content:n,params:r}),pl({content:n,transform:K(K({},_t),a),title:s,extra:{attributes:f,styles:g,classes:["".concat(X.cssPrefix,"-layers-text")].concat($r(c))}})})}}},provides:function(t){t.generateLayersText=function(n,r){var i=r.title,a=r.transform,o=r.extra,s=null,l=null;if(ju){var c=parseInt(getComputedStyle(n).fontSize,10),u=n.getBoundingClientRect();s=u.width/c,l=u.height/c}return X.autoA11y&&!i&&(o.attributes["aria-hidden"]="true"),Promise.resolve([n,pl({content:n.innerHTML,width:s,height:l,transform:a,title:i,extra:o,watchable:!0})])}}},Ov=new RegExp('"',"ug"),yl=[1105920,1112319];function Sv(e){var t=e.replace(Ov,""),n=Hg(t,0),r=n>=yl[0]&&n<=yl[1],i=t.length===2?t[0]===t[1]:!1;return{value:Ba(i?t[0]:t),isSecondary:r||i}}function wl(e,t){var n="".concat(hg).concat(t.replace(":","-"));return new Promise(function(r,i){if(e.getAttribute(n)!==null)return r();var a=Vn(e.children),o=a.filter(function(w){return w.getAttribute(Ha)===t})[0],s=Kt.getComputedStyle(e,t),l=s.getPropertyValue("font-family").match(yg),c=s.getPropertyValue("font-weight"),u=s.getPropertyValue("content");if(o&&!l)return e.removeChild(o),r();if(l&&u!=="none"&&u!==""){var f=s.getPropertyValue("content"),d=~["Sharp"].indexOf(l[2])?Ne:Ie,g=~["Solid","Regular","Light","Thin","Duotone","Brands","Kit"].indexOf(l[2])?xr[d][l[2].toLowerCase()]:wg[d][c],m=Sv(f),y=m.value,k=m.isSecondary,h=l[0].startsWith("FontAwesome"),v=Lo(g,y),O=v;if(h){var S=Kg(y);S.iconName&&S.prefix&&(v=S.iconName,g=S.prefix)}if(v&&!k&&(!o||o.getAttribute(Ao)!==g||o.getAttribute(To)!==O)){e.setAttribute(n,O),o&&e.removeChild(o);var $=vv(),I=$.extra;I.attributes[Ha]=t,Ya(v,g).then(function(w){var _=No(K(K({},$),{},{icons:{main:w,mask:Ro()},prefix:g,iconName:O,extra:I,watchable:!0})),x=Me.createElement("svg");t==="::before"?e.insertBefore(x,e.firstChild):e.appendChild(x),x.outerHTML=_.map(function(T){return Rr(T)}).join(` +`),e.removeAttribute(n),r()}).catch(i)}else r()}else r()})}function Av(e){return Promise.all([wl(e,"::before"),wl(e,"::after")])}function Tv(e){return e.parentNode!==document.head&&!~vg.indexOf(e.tagName.toUpperCase())&&!e.getAttribute(Ha)&&(!e.parentNode||e.parentNode.tagName!=="svg")}function kl(e){if(Mt)return new Promise(function(t,n){var r=Vn(e.querySelectorAll("*")).filter(Tv).map(Av),i=Do.begin("searchPseudoElements");of(),Promise.all(r).then(function(){i(),qa(),t()}).catch(function(){i(),qa(),n()})})}var Pv={hooks:function(){return{mutationObserverCallbacks:function(n){return n.pseudoElementsCallback=kl,n}}},provides:function(t){t.pseudoElements2svg=function(n){var r=n.node,i=r===void 0?Me:r;X.searchPseudoElements&&kl(i)}}},xl=!1,Iv={mixout:function(){return{dom:{unwatch:function(){of(),xl=!0}}}},hooks:function(){return{bootstrap:function(){vl(Ua("mutationObserverCallbacks",{}))},noAuto:function(){pv()},watch:function(n){var r=n.observeMutationsRoot;xl?qa():vl(Ua("mutationObserverCallbacks",{observeMutationsRoot:r}))}}}},El=function(t){var n={size:16,x:0,y:0,flipX:!1,flipY:!1,rotate:0};return t.toLowerCase().split(" ").reduce(function(r,i){var a=i.toLowerCase().split("-"),o=a[0],s=a.slice(1).join("-");if(o&&s==="h")return r.flipX=!0,r;if(o&&s==="v")return r.flipY=!0,r;if(s=parseFloat(s),isNaN(s))return r;switch(o){case"grow":r.size=r.size+s;break;case"shrink":r.size=r.size-s;break;case"left":r.x=r.x-s;break;case"right":r.x=r.x+s;break;case"up":r.y=r.y-s;break;case"down":r.y=r.y+s;break;case"rotate":r.rotate=r.rotate+s;break}return r},n)},Mv={mixout:function(){return{parse:{transform:function(n){return El(n)}}}},hooks:function(){return{parseNodeAttributes:function(n,r){var i=r.getAttribute("data-fa-transform");return i&&(n.transform=El(i)),n}}},provides:function(t){t.generateAbstractTransformGrouping=function(n){var r=n.main,i=n.transform,a=n.containerWidth,o=n.iconWidth,s={transform:"translate(".concat(a/2," 256)")},l="translate(".concat(i.x*32,", ").concat(i.y*32,") "),c="scale(".concat(i.size/16*(i.flipX?-1:1),", ").concat(i.size/16*(i.flipY?-1:1),") "),u="rotate(".concat(i.rotate," 0 0)"),f={transform:"".concat(l," ").concat(c," ").concat(u)},d={transform:"translate(".concat(o/2*-1," -256)")},g={outer:s,inner:f,path:d};return{tag:"g",attributes:K({},g.outer),children:[{tag:"g",attributes:K({},g.inner),children:[{tag:r.icon.tag,children:r.icon.children,attributes:K(K({},r.icon.attributes),g.path)}]}]}}}},va={x:0,y:0,width:"100%",height:"100%"};function Cl(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return e.attributes&&(e.attributes.fill||t)&&(e.attributes.fill="black"),e}function $v(e){return e.tag==="g"?e.children:[e]}var Lv={hooks:function(){return{parseNodeAttributes:function(n,r){var i=r.getAttribute("data-fa-mask"),a=i?Ji(i.split(" ").map(function(o){return o.trim()})):Ro();return a.prefix||(a.prefix=qt()),n.mask=a,n.maskId=r.getAttribute("data-fa-mask-id"),n}}},provides:function(t){t.generateAbstractMask=function(n){var r=n.children,i=n.attributes,a=n.main,o=n.mask,s=n.maskId,l=n.transform,c=a.width,u=a.icon,f=o.width,d=o.icon,g=Mg({transform:l,containerWidth:f,iconWidth:c}),m={tag:"rect",attributes:K(K({},va),{},{fill:"white"})},y=u.children?{children:u.children.map(Cl)}:{},k={tag:"g",attributes:K({},g.inner),children:[Cl(K({tag:u.tag,attributes:K(K({},u.attributes),g.path)},y))]},h={tag:"g",attributes:K({},g.outer),children:[k]},v="mask-".concat(s||Or()),O="clip-".concat(s||Or()),S={tag:"mask",attributes:K(K({},va),{},{id:v,maskUnits:"userSpaceOnUse",maskContentUnits:"userSpaceOnUse"}),children:[m,h]},$={tag:"defs",children:[{tag:"clipPath",attributes:{id:O},children:$v(d)},S]};return r.push($,{tag:"rect",attributes:K({fill:"currentColor","clip-path":"url(#".concat(O,")"),mask:"url(#".concat(v,")")},va)}),{children:r,attributes:i}}}},Rv={provides:function(t){var n=!1;Kt.matchMedia&&(n=Kt.matchMedia("(prefers-reduced-motion: reduce)").matches),t.missingIconAbstract=function(){var r=[],i={fill:"currentColor"},a={attributeType:"XML",repeatCount:"indefinite",dur:"2s"};r.push({tag:"path",attributes:K(K({},i),{},{d:"M156.5,447.7l-12.6,29.5c-18.7-9.5-35.9-21.2-51.5-34.9l22.7-22.7C127.6,430.5,141.5,440,156.5,447.7z M40.6,272H8.5 c1.4,21.2,5.4,41.7,11.7,61.1L50,321.2C45.1,305.5,41.8,289,40.6,272z M40.6,240c1.4-18.8,5.2-37,11.1-54.1l-29.5-12.6 C14.7,194.3,10,216.7,8.5,240H40.6z M64.3,156.5c7.8-14.9,17.2-28.8,28.1-41.5L69.7,92.3c-13.7,15.6-25.5,32.8-34.9,51.5 L64.3,156.5z M397,419.6c-13.9,12-29.4,22.3-46.1,30.4l11.9,29.8c20.7-9.9,39.8-22.6,56.9-37.6L397,419.6z M115,92.4 c13.9-12,29.4-22.3,46.1-30.4l-11.9-29.8c-20.7,9.9-39.8,22.6-56.8,37.6L115,92.4z M447.7,355.5c-7.8,14.9-17.2,28.8-28.1,41.5 l22.7,22.7c13.7-15.6,25.5-32.9,34.9-51.5L447.7,355.5z M471.4,272c-1.4,18.8-5.2,37-11.1,54.1l29.5,12.6 c7.5-21.1,12.2-43.5,13.6-66.8H471.4z M321.2,462c-15.7,5-32.2,8.2-49.2,9.4v32.1c21.2-1.4,41.7-5.4,61.1-11.7L321.2,462z M240,471.4c-18.8-1.4-37-5.2-54.1-11.1l-12.6,29.5c21.1,7.5,43.5,12.2,66.8,13.6V471.4z M462,190.8c5,15.7,8.2,32.2,9.4,49.2h32.1 c-1.4-21.2-5.4-41.7-11.7-61.1L462,190.8z M92.4,397c-12-13.9-22.3-29.4-30.4-46.1l-29.8,11.9c9.9,20.7,22.6,39.8,37.6,56.9 L92.4,397z M272,40.6c18.8,1.4,36.9,5.2,54.1,11.1l12.6-29.5C317.7,14.7,295.3,10,272,8.5V40.6z M190.8,50 c15.7-5,32.2-8.2,49.2-9.4V8.5c-21.2,1.4-41.7,5.4-61.1,11.7L190.8,50z M442.3,92.3L419.6,115c12,13.9,22.3,29.4,30.5,46.1 l29.8-11.9C470,128.5,457.3,109.4,442.3,92.3z M397,92.4l22.7-22.7c-15.6-13.7-32.8-25.5-51.5-34.9l-12.6,29.5 C370.4,72.1,384.4,81.5,397,92.4z"})});var o=K(K({},a),{},{attributeName:"opacity"}),s={tag:"circle",attributes:K(K({},i),{},{cx:"256",cy:"364",r:"28"}),children:[]};return n||s.children.push({tag:"animate",attributes:K(K({},a),{},{attributeName:"r",values:"28;14;28;28;14;28;"})},{tag:"animate",attributes:K(K({},o),{},{values:"1;0;1;1;0;1;"})}),r.push(s),r.push({tag:"path",attributes:K(K({},i),{},{opacity:"1",d:"M263.7,312h-16c-6.6,0-12-5.4-12-12c0-71,77.4-63.9,77.4-107.8c0-20-17.8-40.2-57.4-40.2c-29.1,0-44.3,9.6-59.2,28.7 c-3.9,5-11.1,6-16.2,2.4l-13.1-9.2c-5.6-3.9-6.9-11.8-2.6-17.2c21.2-27.2,46.4-44.7,91.2-44.7c52.3,0,97.4,29.8,97.4,80.2 c0,67.6-77.4,63.5-77.4,107.8C275.7,306.6,270.3,312,263.7,312z"}),children:n?[]:[{tag:"animate",attributes:K(K({},o),{},{values:"1;0;0;0;0;1;"})}]}),n||r.push({tag:"path",attributes:K(K({},i),{},{opacity:"0",d:"M232.5,134.5l7,168c0.3,6.4,5.6,11.5,12,11.5h9c6.4,0,11.7-5.1,12-11.5l7-168c0.3-6.8-5.2-12.5-12-12.5h-23 C237.7,122,232.2,127.7,232.5,134.5z"}),children:[{tag:"animate",attributes:K(K({},o),{},{values:"0;0;1;1;0;0;"})}]}),{tag:"g",attributes:{class:"missing"},children:r}}}},Nv={hooks:function(){return{parseNodeAttributes:function(n,r){var i=r.getAttribute("data-fa-symbol"),a=i===null?!1:i===""?!0:i;return n.symbol=a,n}}}},Dv=[Rg,kv,xv,Ev,Cv,Pv,Iv,Mv,Lv,Rv,Nv];Qg(Dv,{mixoutsTo:et});et.noAuto;var lf=et.config;et.library;et.dom;var Ci=et.parse;et.findIconDefinition;et.toHtml;var jv=et.icon;et.layer;var Fv=et.text;et.counter;function Ol(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function st(e){for(var t=1;t=0)&&(n[i]=e[i]);return n}function Bv(e,t){if(e==null)return{};var n=Hv(e,t),r,i;if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0)&&Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}function Ga(e){return zv(e)||Uv(e)||Vv(e)||Wv()}function zv(e){if(Array.isArray(e))return Qa(e)}function Uv(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function Vv(e,t){if(e){if(typeof e=="string")return Qa(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if(n==="Object"&&e.constructor&&(n=e.constructor.name),n==="Map"||n==="Set")return Array.from(e);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Qa(e,t)}}function Qa(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1&&arguments[1]!==void 0?arguments[1]:{},n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};if(typeof e=="string")return e;var r=(e.children||[]).map(function(l){return Fo(l)}),i=Object.keys(e.attributes||{}).reduce(function(l,c){var u=e.attributes[c];switch(c){case"class":l.class=Qv(u);break;case"style":l.style=Gv(u);break;default:l.attrs[c]=u}return l},{attrs:{},class:{},style:{}});n.class;var a=n.style,o=a===void 0?{}:a,s=Bv(n,qv);return Pr(e.tag,st(st(st({},t),{},{class:i.class,style:st(st({},i.style),o)},i.attrs),s),r)}var uf=!1;try{uf=!0}catch{}function Xv(){if(!uf&&console&&typeof console.error=="function"){var e;(e=console).error.apply(e,arguments)}}function dr(e,t){return Array.isArray(t)&&t.length>0||!Array.isArray(t)&&t?Ke({},e,t):{}}function Jv(e){var t,n=(t={"fa-spin":e.spin,"fa-pulse":e.pulse,"fa-fw":e.fixedWidth,"fa-border":e.border,"fa-li":e.listItem,"fa-inverse":e.inverse,"fa-flip":e.flip===!0,"fa-flip-horizontal":e.flip==="horizontal"||e.flip==="both","fa-flip-vertical":e.flip==="vertical"||e.flip==="both"},Ke(t,"fa-".concat(e.size),e.size!==null),Ke(t,"fa-rotate-".concat(e.rotation),e.rotation!==null),Ke(t,"fa-pull-".concat(e.pull),e.pull!==null),Ke(t,"fa-swap-opacity",e.swapOpacity),Ke(t,"fa-bounce",e.bounce),Ke(t,"fa-shake",e.shake),Ke(t,"fa-beat",e.beat),Ke(t,"fa-fade",e.fade),Ke(t,"fa-beat-fade",e.beatFade),Ke(t,"fa-flash",e.flash),Ke(t,"fa-spin-pulse",e.spinPulse),Ke(t,"fa-spin-reverse",e.spinReverse),t);return Object.keys(n).map(function(r){return n[r]?r:null}).filter(function(r){return r})}function Sl(e){if(e&&Oi(e)==="object"&&e.prefix&&e.iconName&&e.icon)return e;if(Ci.icon)return Ci.icon(e);if(e===null)return null;if(Oi(e)==="object"&&e.prefix&&e.iconName)return e;if(Array.isArray(e)&&e.length===2)return{prefix:e[0],iconName:e[1]};if(typeof e=="string")return{prefix:"fas",iconName:e}}var Pt=he({name:"FontAwesomeIcon",props:{border:{type:Boolean,default:!1},fixedWidth:{type:Boolean,default:!1},flip:{type:[Boolean,String],default:!1,validator:function(t){return[!0,!1,"horizontal","vertical","both"].indexOf(t)>-1}},icon:{type:[Object,Array,String],required:!0},mask:{type:[Object,Array,String],default:null},listItem:{type:Boolean,default:!1},pull:{type:String,default:null,validator:function(t){return["right","left"].indexOf(t)>-1}},pulse:{type:Boolean,default:!1},rotation:{type:[String,Number],default:null,validator:function(t){return[90,180,270].indexOf(Number.parseInt(t,10))>-1}},swapOpacity:{type:Boolean,default:!1},size:{type:String,default:null,validator:function(t){return["2xs","xs","sm","lg","xl","2xl","1x","2x","3x","4x","5x","6x","7x","8x","9x","10x"].indexOf(t)>-1}},spin:{type:Boolean,default:!1},transform:{type:[String,Object],default:null},symbol:{type:[Boolean,String],default:!1},title:{type:String,default:null},inverse:{type:Boolean,default:!1},bounce:{type:Boolean,default:!1},shake:{type:Boolean,default:!1},beat:{type:Boolean,default:!1},fade:{type:Boolean,default:!1},beatFade:{type:Boolean,default:!1},flash:{type:Boolean,default:!1},spinPulse:{type:Boolean,default:!1},spinReverse:{type:Boolean,default:!1}},setup:function(t,n){var r=n.attrs,i=ce(function(){return Sl(t.icon)}),a=ce(function(){return dr("classes",Jv(t))}),o=ce(function(){return dr("transform",typeof t.transform=="string"?Ci.transform(t.transform):t.transform)}),s=ce(function(){return dr("mask",Sl(t.mask))}),l=ce(function(){return jv(i.value,st(st(st(st({},a.value),o.value),s.value),{},{symbol:t.symbol,title:t.title}))});je(l,function(u){if(!u)return Xv("Could not find one or more icon(s)",i.value,s.value)},{immediate:!0});var c=ce(function(){return l.value?Fo(l.value.abstract[0],{},r):null});return function(){return c.value}}});he({name:"FontAwesomeLayers",props:{fixedWidth:{type:Boolean,default:!1}},setup:function(t,n){var r=n.slots,i=lf.familyPrefix,a=ce(function(){return["".concat(i,"-layers")].concat(Ga(t.fixedWidth?["".concat(i,"-fw")]:[]))});return function(){return Pr("div",{class:a.value},r.default?r.default():[])}}});he({name:"FontAwesomeLayersText",props:{value:{type:[String,Number],default:""},transform:{type:[String,Object],default:null},counter:{type:Boolean,default:!1},position:{type:String,default:null,validator:function(t){return["bottom-left","bottom-right","top-left","top-right"].indexOf(t)>-1}}},setup:function(t,n){var r=n.attrs,i=lf.familyPrefix,a=ce(function(){return dr("classes",[].concat(Ga(t.counter?["".concat(i,"-layers-counter")]:[]),Ga(t.position?["".concat(i,"-layers-").concat(t.position)]:[])))}),o=ce(function(){return dr("transform",typeof t.transform=="string"?Ci.transform(t.transform):t.transform)}),s=ce(function(){var c=Fv(t.value.toString(),st(st({},o.value),a.value)),u=c.abstract;return t.counter&&(u[0].attributes.class=u[0].attributes.class.replace("fa-layers-text","")),u[0]}),l=ce(function(){return Fo(s.value,{},r)});return function(){return l.value}}});const Ho=he({__name:"LinkIcon",props:{icon:null},setup(e){return(t,n)=>{const r=Mr;return te(),ke(r,{class:"link-effect inline-flex h-5 w-5 items-center justify-center rounded-full text-lg after:inset-[-.3rem] after:rounded-full after:bg-black-400/10"},{default:_e(()=>[ee(ae(Pt),{icon:e.icon},null,8,["icon"])]),_:1})}}}),Wn=(e,t)=>{const n=e.__vccOpts||e;for(const[r,i]of t)n[r]=i;return n},Zv={};function e1(e,t){const n=Mr;return te(),ke(n,{class:"link-effect inline-block whitespace-nowrap font-semibold leading-none after:inset-x-[-8px] after:inset-y-[-6px] after:rounded-lg after:bg-black-400/10"},{default:_e(()=>[Zt(e.$slots,"default")]),_:3})}const Bo=Wn(Zv,[["render",e1]]);var t1={prefix:"fas",iconName:"file-lines",icon:[384,512,[128441,128462,61686,"file-alt","file-text"],"f15c","M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM112 256H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 64H272c8.8 0 16 7.2 16 16s-7.2 16-16 16H112c-8.8 0-16-7.2-16-16s7.2-16 16-16z"]},Al=t1,n1={prefix:"fas",iconName:"caret-right",icon:[256,512,[],"f0da","M246.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 256c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l128-128z"]},r1={prefix:"fas",iconName:"bars",icon:[448,512,["navicon"],"f0c9","M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"]},ff={prefix:"fas",iconName:"up-right-from-square",icon:[448,512,["external-link-alt"],"f35d","M288 32c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9L306.7 128 169.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L352 173.3l41.4 41.4c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V64c0-17.7-14.3-32-32-32H288zM80 64C35.8 64 0 99.8 0 144V400c0 44.2 35.8 80 80 80H336c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v80c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16h80c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"]},i1={prefix:"fas",iconName:"envelope",icon:[512,512,[128386,9993,61443],"f0e0","M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"]},a1={prefix:"fas",iconName:"sun",icon:[512,512,[9728],"f185","M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM352 256c0 53-43 96-96 96s-96-43-96-96s43-96 96-96s96 43 96 96zm32 0c0-70.7-57.3-128-128-128s-128 57.3-128 128s57.3 128 128 128s128-57.3 128-128z"]},df={prefix:"fas",iconName:"xmark",icon:[320,512,[128473,10005,10006,10060,215,"close","multiply","remove","times"],"f00d","M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"]},o1={prefix:"fas",iconName:"moon",icon:[384,512,[127769,9214],"f186","M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"]},s1={prefix:"fab",iconName:"discord",icon:[640,512,[],"f392","M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z"]},l1={prefix:"fab",iconName:"linkedin",icon:[448,512,[],"f08c","M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"]},li={prefix:"fab",iconName:"github",icon:[496,512,[],"f09b","M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"]};const bt={cakeResume:{icon:Al,url:"https://www.cakeresume.com/ngseke",title:"CakeResume"},hackmd:{icon:Al,url:"https://hackmd.io/@xq",title:"HackMD Notes"},github:{icon:li,url:"https://github.com/ngseke",title:"GitHub"},githubNgsekeMe:{icon:li,url:"https://github.com/ngseke/ngseke.me",title:"GitHub: ngseke.me"},githubGist:{icon:li,url:"https://gist.github.com/ngseke",title:"GitHub Gist"},linkedin:{icon:l1,url:"https://www.linkedin.com/in/ngseke/",title:"LinkedIn"},email:{icon:i1,url:"mailto:ngseke@gmail.com",title:"Email"},discord:{icon:s1,url:"https://discordapp.com/users/533670703874965520",title:"Discord"}},c1={class:"border-t border-black-300 text-black-700 dark:border-black-700 dark:text-black-300"},u1={class:"container flex min-h-[8rem] flex-col justify-center p-4"},f1={class:"flex w-full justify-between"},d1=Re(" ngseke.me "),p1={class:"space-x-3"},m1=he({__name:"TheFooter",setup(e){const t=new Date().getFullYear(),n=[bt.email,bt.linkedin,bt.githubNgsekeMe];return(r,i)=>{const a=Bo,o=Ho;return te(),Oe("footer",c1,[Z("div",u1,[Z("div",f1,[Z("div",null,[Re(" © 2018 - "+Xe(ae(t))+" ",1),ee(a,{class:"ml-1",href:"/"},{default:_e(()=>[d1]),_:1})]),Z("div",p1,[(te(),Oe(Le,null,Jt(n,s=>ee(o,{key:s.url,href:s.url,icon:s.icon,title:s.title},null,8,["href","icon","title"])),64))])])])])}}}),zo=he({__name:"ButtonRotateIcon",props:{size:{default:"md"}},setup(e){return(t,n)=>(te(),Oe("button",{class:yt(["link-effect select-none rounded-full after:inset-[-.3rem] after:rounded-full after:bg-black-400/10",{"h-8 w-8 text-xl":e.size==="md","h-10 w-10 text-3xl":e.size==="lg"}]),type:"button"},[ee(Yi,{enterActiveClass:"transition-all duration-400",enterFromClass:"opacity-0 -rotate-45",leaveActiveClass:"transition-all duration-100",leaveToClass:"opacity-0 rotate-45",mode:"out-in"},{default:_e(()=>[Zt(t.$slots,"default")]),_:3})],2))}});var Tl;const Yn=typeof window<"u",h1=e=>typeof e=="function",g1=e=>typeof e=="string",pr=()=>{},Xa=Yn&&((Tl=window==null?void 0:window.navigator)==null?void 0:Tl.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent);function _n(e){return typeof e=="function"?e():ae(e)}function v1(e,t){function n(...r){return new Promise((i,a)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(i).catch(a)})}return n}const pf=e=>e();function b1(e=pf){const t=Ce(!0);function n(){t.value=!1}function r(){t.value=!0}const i=(...a)=>{t.value&&e(...a)};return{isActive:Di(t),pause:n,resume:r,eventFilter:i}}function _1(e){return e}function Kn(e){return rd()?(id(e),!0):!1}function mf(e){return typeof e=="function"?ce(e):Ce(e)}function ea(e,t=!0){xo()?Xt(e):t?e():Qt(e)}function hf(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,i=Be(e),a=Ce(e);function o(s){if(arguments.length)return a.value=s,a.value;{const l=_n(n);return a.value=a.value===l?_n(r):l,a.value}}return i?o:[a,o]}var Pl=Object.getOwnPropertySymbols,y1=Object.prototype.hasOwnProperty,w1=Object.prototype.propertyIsEnumerable,k1=(e,t)=>{var n={};for(var r in e)y1.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&Pl)for(var r of Pl(e))t.indexOf(r)<0&&w1.call(e,r)&&(n[r]=e[r]);return n};function x1(e,t,n={}){const r=n,{eventFilter:i=pf}=r,a=k1(r,["eventFilter"]);return je(e,v1(i,t),a)}var E1=Object.defineProperty,C1=Object.defineProperties,O1=Object.getOwnPropertyDescriptors,Si=Object.getOwnPropertySymbols,gf=Object.prototype.hasOwnProperty,vf=Object.prototype.propertyIsEnumerable,Il=(e,t,n)=>t in e?E1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,S1=(e,t)=>{for(var n in t||(t={}))gf.call(t,n)&&Il(e,n,t[n]);if(Si)for(var n of Si(t))vf.call(t,n)&&Il(e,n,t[n]);return e},A1=(e,t)=>C1(e,O1(t)),T1=(e,t)=>{var n={};for(var r in e)gf.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&Si)for(var r of Si(e))t.indexOf(r)<0&&vf.call(e,r)&&(n[r]=e[r]);return n};function P1(e,t,n={}){const r=n,{eventFilter:i}=r,a=T1(r,["eventFilter"]),{eventFilter:o,pause:s,resume:l,isActive:c}=b1(i);return{stop:x1(e,t,A1(S1({},a),{eventFilter:o})),pause:s,resume:l,isActive:c}}function zt(e){var t;const n=_n(e);return(t=n==null?void 0:n.$el)!=null?t:n}const pt=Yn?window:void 0,I1=Yn?window.document:void 0;function Et(...e){let t,n,r,i;if(g1(e[0])||Array.isArray(e[0])?([n,r,i]=e,t=pt):[t,n,r,i]=e,!t)return pr;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const a=[],o=()=>{a.forEach(u=>u()),a.length=0},s=(u,f,d,g)=>(u.addEventListener(f,d,g),()=>u.removeEventListener(f,d,g)),l=je(()=>[zt(t),_n(i)],([u,f])=>{o(),u&&a.push(...n.flatMap(d=>r.map(g=>s(u,d,g,f))))},{immediate:!0,flush:"post"}),c=()=>{l(),o()};return Kn(c),c}let Ml=!1;function M1(e,t,n={}){const{window:r=pt,ignore:i=[],capture:a=!0,detectIframe:o=!1}=n;if(!r)return;Xa&&!Ml&&(Ml=!0,Array.from(r.document.body.children).forEach(d=>d.addEventListener("click",pr)));let s=!0;const l=d=>i.some(g=>{if(typeof g=="string")return Array.from(r.document.querySelectorAll(g)).some(m=>m===d.target||d.composedPath().includes(m));{const m=zt(g);return m&&(d.target===m||d.composedPath().includes(m))}}),u=[Et(r,"click",d=>{const g=zt(e);if(!(!g||g===d.target||d.composedPath().includes(g))){if(d.detail===0&&(s=!l(d)),!s){s=!0;return}t(d)}},{passive:!0,capture:a}),Et(r,"pointerdown",d=>{const g=zt(e);g&&(s=!d.composedPath().includes(g)&&!l(d))},{passive:!0}),o&&Et(r,"blur",d=>{var g;const m=zt(e);((g=r.document.activeElement)==null?void 0:g.tagName)==="IFRAME"&&!(m!=null&&m.contains(r.document.activeElement))&&t(d)})].filter(Boolean);return()=>u.forEach(d=>d())}function bf(e,t=!1){const n=Ce(),r=()=>n.value=!!e();return r(),ea(r,t),n}function $1(e,t={}){const{window:n=pt}=t,r=bf(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let i;const a=Ce(!1),o=()=>{i&&("removeEventListener"in i?i.removeEventListener("change",s):i.removeListener(s))},s=()=>{r.value&&(o(),i=n.matchMedia(mf(e).value),a.value=i.matches,"addEventListener"in i?i.addEventListener("change",s):i.addListener(s))};return Nc(s),Kn(()=>o()),a}const Ja=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},Za="__vueuse_ssr_handlers__";Ja[Za]=Ja[Za]||{};const L1=Ja[Za];function _f(e,t){return L1[e]||t}function R1(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"}var N1=Object.defineProperty,$l=Object.getOwnPropertySymbols,D1=Object.prototype.hasOwnProperty,j1=Object.prototype.propertyIsEnumerable,Ll=(e,t,n)=>t in e?N1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Rl=(e,t)=>{for(var n in t||(t={}))D1.call(t,n)&&Ll(e,n,t[n]);if($l)for(var n of $l(t))j1.call(t,n)&&Ll(e,n,t[n]);return e};const F1={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()}},Nl="vueuse-storage";function yf(e,t,n,r={}){var i;const{flush:a="pre",deep:o=!0,listenToStorageChanges:s=!0,writeDefaults:l=!0,mergeDefaults:c=!1,shallow:u,window:f=pt,eventFilter:d,onError:g=_=>{console.error(_)}}=r,m=(u?Sc:Ce)(t);if(!n)try{n=_f("getDefaultStorage",()=>{var _;return(_=pt)==null?void 0:_.localStorage})()}catch(_){g(_)}if(!n)return m;const y=_n(t),k=R1(y),h=(i=r.serializer)!=null?i:F1[k],{pause:v,resume:O}=P1(m,()=>S(m.value),{flush:a,deep:o,eventFilter:d});return f&&s&&(Et(f,"storage",w),Et(f,Nl,I)),w(),m;function S(_){try{if(_==null)n.removeItem(e);else{const x=h.write(_),T=n.getItem(e);T!==x&&(n.setItem(e,x),f&&f.dispatchEvent(new CustomEvent(Nl,{detail:{key:e,oldValue:T,newValue:x,storageArea:n}})))}}catch(x){g(x)}}function $(_){const x=_?_.newValue:n.getItem(e);if(x==null)return l&&y!==null&&n.setItem(e,h.write(y)),y;if(!_&&c){const T=h.read(x);return h1(c)?c(T,y):k==="object"&&!Array.isArray(T)?Rl(Rl({},y),T):T}else return typeof x!="string"?x:h.read(x)}function I(_){w(_.detail)}function w(_){if(!(_&&_.storageArea!==n)){if(_&&_.key==null){m.value=y;return}if(!(_&&_.key!==e)){v();try{m.value=$(_)}catch(x){g(x)}finally{_?Qt(O):O()}}}}}function wf(e){return $1("(prefers-color-scheme: dark)",e)}var H1=Object.defineProperty,Dl=Object.getOwnPropertySymbols,B1=Object.prototype.hasOwnProperty,z1=Object.prototype.propertyIsEnumerable,jl=(e,t,n)=>t in e?H1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,U1=(e,t)=>{for(var n in t||(t={}))B1.call(t,n)&&jl(e,n,t[n]);if(Dl)for(var n of Dl(t))z1.call(t,n)&&jl(e,n,t[n]);return e};function V1(e={}){const{selector:t="html",attribute:n="class",initialValue:r="auto",window:i=pt,storage:a,storageKey:o="vueuse-color-scheme",listenToStorageChanges:s=!0,storageRef:l,emitAuto:c}=e,u=U1({auto:"",light:"light",dark:"dark"},e.modes||{}),f=wf({window:i}),d=ce(()=>f.value?"dark":"light"),g=l||(o==null?Ce(r):yf(o,r,a,{window:i,listenToStorageChanges:s})),m=ce({get(){return g.value==="auto"&&!c?d.value:g.value},set(v){g.value=v}}),y=_f("updateHTMLAttrs",(v,O,S)=>{const $=i==null?void 0:i.document.querySelector(v);if($)if(O==="class"){const I=S.split(/\s/g);Object.values(u).flatMap(w=>(w||"").split(/\s/g)).filter(Boolean).forEach(w=>{I.includes(w)?$.classList.add(w):$.classList.remove(w)})}else $.setAttribute(O,S)});function k(v){var O;const S=v==="auto"?d.value:v;y(t,n,(O=u[S])!=null?O:S)}function h(v){e.onChanged?e.onChanged(v,k):k(v)}return je(m,h,{flush:"post",immediate:!0}),c&&je(d,()=>h(m.value),{flush:"post"}),ea(()=>h(m.value)),m}var W1=Object.defineProperty,Y1=Object.defineProperties,K1=Object.getOwnPropertyDescriptors,Fl=Object.getOwnPropertySymbols,q1=Object.prototype.hasOwnProperty,G1=Object.prototype.propertyIsEnumerable,Hl=(e,t,n)=>t in e?W1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Q1=(e,t)=>{for(var n in t||(t={}))q1.call(t,n)&&Hl(e,n,t[n]);if(Fl)for(var n of Fl(t))G1.call(t,n)&&Hl(e,n,t[n]);return e},X1=(e,t)=>Y1(e,K1(t));function J1(e={}){const{valueDark:t="dark",valueLight:n="",window:r=pt}=e,i=V1(X1(Q1({},e),{onChanged:(s,l)=>{var c;e.onChanged?(c=e.onChanged)==null||c.call(e,s==="dark"):l(s)},modes:{dark:t,light:n}})),a=wf({window:r});return ce({get(){return i.value==="dark"},set(s){s===a.value?i.value="auto":i.value=s?"dark":"light"}})}function Z1(e,t,n={}){const{root:r,rootMargin:i="0px",threshold:a=.1,window:o=pt}=n,s=bf(()=>o&&"IntersectionObserver"in o);let l=pr;const c=s.value?je(()=>({el:zt(e),root:zt(r)}),({el:f,root:d})=>{if(l(),!f)return;const g=new IntersectionObserver(t,{root:d,rootMargin:i,threshold:a});g.observe(f),l=()=>{g.disconnect(),l=pr}},{immediate:!0,flush:"post"}):pr,u=()=>{l(),c()};return Kn(u),{isSupported:s,stop:u}}function eb(e,t,n={}){const{window:r=pt}=n;return yf(e,t,r==null?void 0:r.localStorage,n)}function tb(){const e=Ce(!1);return Xt(()=>{e.value=!0}),e}var Bl;(function(e){e.UP="UP",e.RIGHT="RIGHT",e.DOWN="DOWN",e.LEFT="LEFT",e.NONE="NONE"})(Bl||(Bl={}));function kf(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientHeight1?!0:(t.preventDefault&&t.preventDefault(),!1)}function rb(e,t=!1){const n=Ce(t);let r=null,i;je(mf(e),s=>{if(s){const l=s;i=l.style.overflow,n.value&&(l.style.overflow="hidden")}},{immediate:!0});const a=()=>{const s=_n(e);!s||n.value||(Xa&&(r=Et(s,"touchmove",l=>{nb(l)},{passive:!1})),s.style.overflow="hidden",n.value=!0)},o=()=>{const s=_n(e);!s||!n.value||(Xa&&(r==null||r()),s.style.overflow=i,n.value=!1)};return Kn(o),ce({get(){return n.value},set(s){s?a():o()}})}let ib=0;function ab(e,t={}){const n=Ce(!1),{document:r=I1,immediate:i=!0,manual:a=!1,id:o=`vueuse_styletag_${++ib}`}=t,s=Ce(e);let l=()=>{};const c=()=>{if(!r)return;const f=r.getElementById(o)||r.createElement("style");f.isConnected||(f.type="text/css",f.id=o,t.media&&(f.media=t.media),r.head.appendChild(f)),!n.value&&(l=je(s,d=>{f.textContent=d},{immediate:!0}),n.value=!0)},u=()=>{!r||!n.value||(l(),r.head.removeChild(r.getElementById(o)),n.value=!1)};return i&&!a&&ea(c),a||Kn(u),{id:o,css:s,unload:u,load:c,isLoaded:Di(n)}}var ob=Object.defineProperty,zl=Object.getOwnPropertySymbols,sb=Object.prototype.hasOwnProperty,lb=Object.prototype.propertyIsEnumerable,Ul=(e,t,n)=>t in e?ob(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,cb=(e,t)=>{for(var n in t||(t={}))sb.call(t,n)&&Ul(e,n,t[n]);if(zl)for(var n of zl(t))lb.call(t,n)&&Ul(e,n,t[n]);return e};const ub={easeInSine:[.12,0,.39,0],easeOutSine:[.61,1,.88,1],easeInOutSine:[.37,0,.63,1],easeInQuad:[.11,0,.5,0],easeOutQuad:[.5,1,.89,1],easeInOutQuad:[.45,0,.55,1],easeInCubic:[.32,0,.67,0],easeOutCubic:[.33,1,.68,1],easeInOutCubic:[.65,0,.35,1],easeInQuart:[.5,0,.75,0],easeOutQuart:[.25,1,.5,1],easeInOutQuart:[.76,0,.24,1],easeInQuint:[.64,0,.78,0],easeOutQuint:[.22,1,.36,1],easeInOutQuint:[.83,0,.17,1],easeInExpo:[.7,0,.84,0],easeOutExpo:[.16,1,.3,1],easeInOutExpo:[.87,0,.13,1],easeInCirc:[.55,0,1,.45],easeOutCirc:[0,.55,.45,1],easeInOutCirc:[.85,0,.15,1],easeInBack:[.36,0,.66,-.56],easeOutBack:[.34,1.56,.64,1],easeInOutBack:[.68,-.6,.32,1.6]};cb({linear:_1},ub);function fb({window:e=pt}={}){if(!e)return{x:Ce(0),y:Ce(0)};const t=Ce(e.scrollX),n=Ce(e.scrollY);return Et(e,"scroll",()=>{t.value=e.scrollX,n.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:n}}function db(e={}){const{window:t=pt,initialWidth:n=1/0,initialHeight:r=1/0,listenOrientation:i=!0,includeScrollbar:a=!0}=e,o=Ce(n),s=Ce(r),l=()=>{t&&(a?(o.value=t.innerWidth,s.value=t.innerHeight):(o.value=t.document.documentElement.clientWidth,s.value=t.document.documentElement.clientHeight))};return l(),ea(l),Et("resize",l,{passive:!0}),i&&Et("orientationchange",l,{passive:!0}),{width:o,height:s}}const pb=J1();function mb(){return pb}let hb=(e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce((t,n)=>(n&=63,n<36?t+=n.toString(36):n<62?t+=(n-26).toString(36).toUpperCase():n>62?t+="-":t+="_",t),"");function gb(){const e=Ce([0,0]),t=hb(),n=`clip-${t}`,r=`--x-${t}`,i=`--y-${t}`;return ab(ce(()=>` + :root { + ${r}: ${e.value[0]}px; + ${i}: ${e.value[1]}px; + } + + @keyframes ${n} { + from { + clip-path: circle(0% at var(${r}) var(${i})); + } + to { + clip-path: circle(150% at var(${r}) var(${i})); + } + } + + ::view-transition-old(root) { + animation: none; + } + + ::view-transition-new(root) { + mix-blend-mode: normal; + animation: ${n} .8s; + } + `)),{startViewTransition:(o,s)=>{document.startViewTransition?(e.value=s,document.startViewTransition(o)):o()}}}const vb=he({__name:"ButtonDarkMode",setup(e){const t=mb(),n=hf(t),{startViewTransition:r}=gb(),i=a=>{r(n,[a.clientX,a.clientY])};return(a,o)=>{const s=zo;return te(),ke(s,{title:ae(t)?"關閉深色模式":"開啟深色模式",onClick:i},{default:_e(()=>[ae(t)?(te(),ke(ae(Pt),{key:0,icon:ae(a1)},null,8,["icon"])):(te(),ke(ae(Pt),{key:1,icon:ae(o1)},null,8,["icon"]))]),_:1},8,["title"])}}}),Uo=he({__name:"TransitionUniversal",props:{enterFromClass:null,leaveToClass:null,enterActiveClass:null,leaveActiveClass:null},setup(e){return(t,n)=>(te(),ke(Yi,{enterActiveClass:e.enterActiveClass,enterFromClass:e.enterFromClass,leaveActiveClass:e.enterActiveClass??e.leaveActiveClass,leaveToClass:e.enterFromClass??e.leaveToClass},{default:_e(()=>[Zt(t.$slots,"default")]),_:3},8,["enterActiveClass","enterFromClass","leaveActiveClass","leaveToClass"]))}}),xf=he({__name:"NavbarLink",setup(e){return(t,n)=>{const r=Mr;return te(),ke(r,{class:"link-effect inline-block uppercase after:-inset-x-2 after:-inset-y-1 after:rounded-lg after:bg-black-400/10"},{default:_e(({isExternal:i})=>[Zt(t.$slots,"default"),i?(te(),ke(ae(Pt),{key:0,class:"ml-2",icon:ae(ff)},null,8,["icon"])):fn("",!0)]),_:3})}}});/*! +* tabbable 6.1.1 +* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE +*/var Ef=["input:not([inert])","select:not([inert])","textarea:not([inert])","a[href]:not([inert])","button:not([inert])","[tabindex]:not(slot):not([inert])","audio[controls]:not([inert])","video[controls]:not([inert])",'[contenteditable]:not([contenteditable="false"]):not([inert])',"details>summary:first-of-type:not([inert])","details:not([inert])"],Ai=Ef.join(","),Cf=typeof Element>"u",yn=Cf?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,Ti=!Cf&&Element.prototype.getRootNode?function(e){var t;return e==null||(t=e.getRootNode)===null||t===void 0?void 0:t.call(e)}:function(e){return e==null?void 0:e.ownerDocument},Pi=function e(t,n){var r;n===void 0&&(n=!0);var i=t==null||(r=t.getAttribute)===null||r===void 0?void 0:r.call(t,"inert"),a=i===""||i==="true",o=a||n&&t&&e(t.parentNode);return o},bb=function(t){var n,r=t==null||(n=t.getAttribute)===null||n===void 0?void 0:n.call(t,"contenteditable");return r===""||r==="true"},Of=function(t,n,r){if(Pi(t))return[];var i=Array.prototype.slice.apply(t.querySelectorAll(Ai));return n&&yn.call(t,Ai)&&i.unshift(t),i=i.filter(r),i},Sf=function e(t,n,r){for(var i=[],a=Array.from(t);a.length;){var o=a.shift();if(!Pi(o,!1))if(o.tagName==="SLOT"){var s=o.assignedElements(),l=s.length?s:o.children,c=e(l,!0,r);r.flatten?i.push.apply(i,c):i.push({scopeParent:o,candidates:c})}else{var u=yn.call(o,Ai);u&&r.filter(o)&&(n||!t.includes(o))&&i.push(o);var f=o.shadowRoot||typeof r.getShadowRoot=="function"&&r.getShadowRoot(o),d=!Pi(f,!1)&&(!r.shadowRootFilter||r.shadowRootFilter(o));if(f&&d){var g=e(f===!0?o.children:f.children,!0,r);r.flatten?i.push.apply(i,g):i.push({scopeParent:o,candidates:g})}else a.unshift.apply(a,o.children)}}return i},Af=function(t,n){return t.tabIndex<0&&(n||/^(AUDIO|VIDEO|DETAILS)$/.test(t.tagName)||bb(t))&&isNaN(parseInt(t.getAttribute("tabindex"),10))?0:t.tabIndex},_b=function(t,n){return t.tabIndex===n.tabIndex?t.documentOrder-n.documentOrder:t.tabIndex-n.tabIndex},Tf=function(t){return t.tagName==="INPUT"},yb=function(t){return Tf(t)&&t.type==="hidden"},wb=function(t){var n=t.tagName==="DETAILS"&&Array.prototype.slice.apply(t.children).some(function(r){return r.tagName==="SUMMARY"});return n},kb=function(t,n){for(var r=0;rsummary:first-of-type"),o=a?t.parentElement:t;if(yn.call(o,"details:not([open]) *"))return!0;if(!r||r==="full"||r==="legacy-full"){if(typeof i=="function"){for(var s=t;t;){var l=t.parentElement,c=Ti(t);if(l&&!l.shadowRoot&&i(l)===!0)return Vl(t);t.assignedSlot?t=t.assignedSlot:!l&&c!==t.ownerDocument?t=c.host:t=l}t=s}if(Ob(t))return!t.getClientRects().length;if(r!=="legacy-full")return!0}else if(r==="non-zero-area")return Vl(t);return!1},Ab=function(t){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(t.tagName))for(var n=t.parentElement;n;){if(n.tagName==="FIELDSET"&&n.disabled){for(var r=0;r=0)},Pb=function e(t){var n=[],r=[];return t.forEach(function(i,a){var o=!!i.scopeParent,s=o?i.scopeParent:i,l=Af(s,o),c=o?e(i.candidates):s;l===0?o?n.push.apply(n,c):n.push(s):r.push({documentOrder:a,tabIndex:l,item:i,isScope:o,content:c})}),r.sort(_b).reduce(function(i,a){return a.isScope?i.push.apply(i,a.content):i.push(a.content),i},[]).concat(n)},Ib=function(t,n){n=n||{};var r;return n.getShadowRoot?r=Sf([t],n.includeContainer,{filter:eo.bind(null,n),flatten:!1,getShadowRoot:n.getShadowRoot,shadowRootFilter:Tb}):r=Of(t,n.includeContainer,eo.bind(null,n)),Pb(r)},Mb=function(t,n){n=n||{};var r;return n.getShadowRoot?r=Sf([t],n.includeContainer,{filter:Ii.bind(null,n),flatten:!0,getShadowRoot:n.getShadowRoot}):r=Of(t,n.includeContainer,Ii.bind(null,n)),r},ei=function(t,n){if(n=n||{},!t)throw new Error("No node provided");return yn.call(t,Ai)===!1?!1:eo(n,t)},$b=Ef.concat("iframe").join(","),Wl=function(t,n){if(n=n||{},!t)throw new Error("No node provided");return yn.call(t,$b)===!1?!1:Ii(n,t)};/*! +* focus-trap 7.3.1 +* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE +*/function Yl(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function Kl(e){for(var t=1;t0){var r=t[t.length-1];r!==n&&r.pause()}var i=t.indexOf(n);i===-1||t.splice(i,1),t.push(n)},deactivateTrap:function(t,n){var r=t.indexOf(n);r!==-1&&t.splice(r,1),t.length>0&&t[t.length-1].unpause()}},Db=function(t){return t.tagName&&t.tagName.toLowerCase()==="input"&&typeof t.select=="function"},jb=function(t){return t.key==="Escape"||t.key==="Esc"||t.keyCode===27},mr=function(t){return t.key==="Tab"||t.keyCode===9},Fb=function(t){return mr(t)&&!t.shiftKey},Hb=function(t){return mr(t)&&t.shiftKey},Gl=function(t){return setTimeout(t,0)},Ql=function(t,n){var r=-1;return t.every(function(i,a){return n(i)?(r=a,!1):!0}),r},Zn=function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),i=1;i1?x-1:0),Y=1;Y=0)w=r.activeElement;else{var _=o.tabbableGroups[0],x=_&&_.firstTabbableNode;w=x||u("fallbackFocus")}if(!w)throw new Error("Your focus-trap needs to have at least one focusable element");return w},d=function(){if(o.containerGroups=o.containers.map(function(w){var _=Ib(w,a.tabbableOptions),x=Mb(w,a.tabbableOptions);return{container:w,tabbableNodes:_,focusableNodes:x,firstTabbableNode:_.length>0?_[0]:null,lastTabbableNode:_.length>0?_[_.length-1]:null,nextTabbableNode:function(Y){var j=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,N=x.findIndex(function(C){return C===Y});if(!(N<0))return j?x.slice(N+1).find(function(C){return ei(C,a.tabbableOptions)}):x.slice(0,N).reverse().find(function(C){return ei(C,a.tabbableOptions)})}}}),o.tabbableGroups=o.containerGroups.filter(function(w){return w.tabbableNodes.length>0}),o.tabbableGroups.length<=0&&!u("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times")},g=function I(w){if(w!==!1&&w!==r.activeElement){if(!w||!w.focus){I(f());return}w.focus({preventScroll:!!a.preventScroll}),o.mostRecentlyFocusedNode=w,Db(w)&&w.select()}},m=function(w){var _=u("setReturnFocus",w);return _||(_===!1?!1:w)},y=function(w){var _=ti(w);if(!(c(_)>=0)){if(Zn(a.clickOutsideDeactivates,w)){s.deactivate({returnFocus:a.returnFocusOnDeactivate});return}Zn(a.allowOutsideClick,w)||w.preventDefault()}},k=function(w){var _=ti(w),x=c(_)>=0;x||_ instanceof Document?x&&(o.mostRecentlyFocusedNode=_):(w.stopImmediatePropagation(),g(o.mostRecentlyFocusedNode||f()))},h=function(w){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1,x=ti(w);d();var T=null;if(o.tabbableGroups.length>0){var Y=c(x),j=Y>=0?o.containerGroups[Y]:void 0;if(Y<0)_?T=o.tabbableGroups[o.tabbableGroups.length-1].lastTabbableNode:T=o.tabbableGroups[0].firstTabbableNode;else if(_){var N=Ql(o.tabbableGroups,function(J){var de=J.firstTabbableNode;return x===de});if(N<0&&(j.container===x||Wl(x,a.tabbableOptions)&&!ei(x,a.tabbableOptions)&&!j.nextTabbableNode(x,!1))&&(N=Y),N>=0){var C=N===0?o.tabbableGroups.length-1:N-1,D=o.tabbableGroups[C];T=D.lastTabbableNode}else mr(w)||(T=j.nextTabbableNode(x,!1))}else{var z=Ql(o.tabbableGroups,function(J){var de=J.lastTabbableNode;return x===de});if(z<0&&(j.container===x||Wl(x,a.tabbableOptions)&&!ei(x,a.tabbableOptions)&&!j.nextTabbableNode(x))&&(z=Y),z>=0){var L=z===o.tabbableGroups.length-1?0:z+1,V=o.tabbableGroups[L];T=V.firstTabbableNode}else mr(w)||(T=j.nextTabbableNode(x))}}else T=u("fallbackFocus");T&&(mr(w)&&w.preventDefault(),g(T))},v=function(w){if(jb(w)&&Zn(a.escapeDeactivates,w)!==!1){w.preventDefault(),s.deactivate();return}(a.isKeyForward(w)||a.isKeyBackward(w))&&h(w,a.isKeyBackward(w))},O=function(w){var _=ti(w);c(_)>=0||Zn(a.clickOutsideDeactivates,w)||Zn(a.allowOutsideClick,w)||(w.preventDefault(),w.stopImmediatePropagation())},S=function(){if(o.active)return ql.activateTrap(i,s),o.delayInitialFocusTimer=a.delayInitialFocus?Gl(function(){g(f())}):g(f()),r.addEventListener("focusin",k,!0),r.addEventListener("mousedown",y,{capture:!0,passive:!1}),r.addEventListener("touchstart",y,{capture:!0,passive:!1}),r.addEventListener("click",O,{capture:!0,passive:!1}),r.addEventListener("keydown",v,{capture:!0,passive:!1}),s},$=function(){if(o.active)return r.removeEventListener("focusin",k,!0),r.removeEventListener("mousedown",y,!0),r.removeEventListener("touchstart",y,!0),r.removeEventListener("click",O,!0),r.removeEventListener("keydown",v,!0),s};return s={get active(){return o.active},get paused(){return o.paused},activate:function(w){if(o.active)return this;var _=l(w,"onActivate"),x=l(w,"onPostActivate"),T=l(w,"checkCanFocusTrap");T||d(),o.active=!0,o.paused=!1,o.nodeFocusedBeforeActivation=r.activeElement,_&&_();var Y=function(){T&&d(),S(),x&&x()};return T?(T(o.containers.concat()).then(Y,Y),this):(Y(),this)},deactivate:function(w){if(!o.active)return this;var _=Kl({onDeactivate:a.onDeactivate,onPostDeactivate:a.onPostDeactivate,checkCanReturnFocus:a.checkCanReturnFocus},w);clearTimeout(o.delayInitialFocusTimer),o.delayInitialFocusTimer=void 0,$(),o.active=!1,o.paused=!1,ql.deactivateTrap(i,s);var x=l(_,"onDeactivate"),T=l(_,"onPostDeactivate"),Y=l(_,"checkCanReturnFocus"),j=l(_,"returnFocus","returnFocusOnDeactivate");x&&x();var N=function(){Gl(function(){j&&g(m(o.nodeFocusedBeforeActivation)),T&&T()})};return j&&Y?(Y(m(o.nodeFocusedBeforeActivation)).then(N,N),this):(N(),this)},pause:function(){return o.paused||!o.active?this:(o.paused=!0,$(),this)},unpause:function(){return!o.paused||!o.active?this:(o.paused=!1,d(),S(),this)},updateContainerElements:function(w){var _=[].concat(w).filter(Boolean);return o.containers=_.map(function(x){return typeof x=="string"?r.querySelector(x):x}),o.active&&d(),this}},s.updateContainerElements(t),s},Ub=Object.defineProperty,Vb=Object.defineProperties,Wb=Object.getOwnPropertyDescriptors,Mi=Object.getOwnPropertySymbols,Pf=Object.prototype.hasOwnProperty,If=Object.prototype.propertyIsEnumerable,Xl=(e,t,n)=>t in e?Ub(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Yb=(e,t)=>{for(var n in t||(t={}))Pf.call(t,n)&&Xl(e,n,t[n]);if(Mi)for(var n of Mi(t))If.call(t,n)&&Xl(e,n,t[n]);return e},Kb=(e,t)=>Vb(e,Wb(t)),qb=(e,t)=>{var n={};for(var r in e)Pf.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&Mi)for(var r of Mi(e))t.indexOf(r)<0&&If.call(e,r)&&(n[r]=e[r]);return n};function Gb(e,t={}){let n;const r=t,{immediate:i}=r,a=qb(r,["immediate"]),o=Ce(!1),s=Ce(!1),l=d=>n&&n.activate(d),c=d=>n&&n.deactivate(d),u=()=>{n&&(n.pause(),s.value=!0)},f=()=>{n&&(n.unpause(),s.value=!1)};return je(()=>zt(e),d=>{d&&(n=zb(d,Kb(Yb({},a),{onActivate(){o.value=!0,t.onActivate&&t.onActivate()},onDeactivate(){o.value=!1,t.onDeactivate&&t.onDeactivate()}})),i&&l())},{flush:"post"}),Kn(()=>c()),{hasFocus:o,isPaused:s,activate:l,deactivate:c,pause:u,unpause:f}}const Qb={key:0,class:"fixed inset-0 z-20 backdrop-blur-sm"},Xb={class:"fixed inset-4 z-30 origin-top-left transition-all duration-200"},Jb={class:"p-4"},Zb={class:"flex-1 overflow-y-auto"},e0={class:"flex flex-col space-y-6 px-8 pb-8 pt-4 text-2xl font-medium dark:text-white/80"},t0=he({__name:"NavbarHamburger",props:{links:null,show:{type:Boolean}},emits:["update:show"],setup(e,{emit:t}){const n=e,r=ce(()=>[{name:"Home",href:{name:"index"}},...n.links]),i=()=>t("update:show",!1),a=Ce(null),{activate:o,deactivate:s}=Gb(a,{allowOutsideClick:!0,onDeactivate:i});return je(()=>n.show,async l=>{await Qt(),l?o():s()}),M1(a,i),(l,c)=>{const u=zo,f=xf,d=Uo;return te(),ke(Op,{to:"body"},[e.show?(te(),Oe("div",Qb)):fn("",!0),ee(d,{enterFromClass:"opacity-0 -translate-4 scale-75"},{default:_e(()=>[pi(Z("div",Xb,[Z("div",{ref_key:"target",ref:a,class:"flex max-h-full flex-col rounded-lg border bg-white/95 backdrop-blur-md dark:border-black-800 dark:bg-black-900/95"},[Z("div",Jb,[ee(u,{class:"dark:text-white/80",size:"lg",onClick:i},{default:_e(()=>[ee(ae(Pt),{icon:ae(df)},null,8,["icon"])]),_:1})]),Z("div",Zb,[Z("ul",e0,[(te(!0),Oe(Le,null,Jt(ae(r),({name:g,href:m,handler:y},k)=>(te(),Oe("li",{key:k},[ee(d,{enterFromClass:"opacity-0 scale-90"},{default:_e(()=>[pi(ee(f,{class:"transition-all",href:m,style:Hn({transitionDelay:`${.06+k*.01}s`}),onClick:h=>{y==null||y(h),l.$emit("update:show",!1)}},{default:_e(()=>[Re(Xe(g),1)]),_:2},1032,["href","style","onClick"]),[[bi,e.show]])]),_:2},1024)]))),128))])])],512)],512),[[bi,e.show]])]),_:1})])}}}),n0=he({__name:"ButtonHamburger",props:{active:{type:Boolean}},setup(e){return(t,n)=>{const r=zo;return te(),ke(r,null,{default:_e(()=>[e.active?(te(),ke(ae(Pt),{key:0,icon:ae(df)},null,8,["icon"])):(te(),ke(ae(Pt),{key:1,icon:ae(r1)},null,8,["icon"]))]),_:1})}}});var ta=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Vo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Mf={};(function(e){e.linear=function(t){return t},e.inQuad=function(t){return t*t},e.outQuad=function(t){return t*(2-t)},e.inOutQuad=function(t){return t*=2,t<1?.5*t*t:-.5*(--t*(t-2)-1)},e.inCube=function(t){return t*t*t},e.outCube=function(t){return--t*t*t+1},e.inOutCube=function(t){return t*=2,t<1?.5*t*t*t:.5*((t-=2)*t*t+2)},e.inQuart=function(t){return t*t*t*t},e.outQuart=function(t){return 1- --t*t*t*t},e.inOutQuart=function(t){return t*=2,t<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},e.inQuint=function(t){return t*t*t*t*t},e.outQuint=function(t){return--t*t*t*t*t+1},e.inOutQuint=function(t){return t*=2,t<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},e.inSine=function(t){return 1-Math.cos(t*Math.PI/2)},e.outSine=function(t){return Math.sin(t*Math.PI/2)},e.inOutSine=function(t){return .5*(1-Math.cos(Math.PI*t))},e.inExpo=function(t){return t==0?0:Math.pow(1024,t-1)},e.outExpo=function(t){return t==1?t:1-Math.pow(2,-10*t)},e.inOutExpo=function(t){return t==0?0:t==1?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(-Math.pow(2,-10*(t-1))+2)},e.inCirc=function(t){return 1-Math.sqrt(1-t*t)},e.outCirc=function(t){return Math.sqrt(1- --t*t)},e.inOutCirc=function(t){return t*=2,t<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},e.inBack=function(t){var n=1.70158;return t*t*((n+1)*t-n)},e.outBack=function(t){var n=1.70158;return--t*t*((n+1)*t+n)+1},e.inOutBack=function(t){var n=2.5949095;return(t*=2)<1?.5*(t*t*((n+1)*t-n)):.5*((t-=2)*t*((n+1)*t+n)+2)},e.inBounce=function(t){return 1-e.outBounce(1-t)},e.outBounce=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},e.inOutBounce=function(t){return t<.5?e.inBounce(t*2)*.5:e.outBounce(t*2-1)*.5+.5},e.inElastic=function(t){var n,r=.1,i=.4;return t===0?0:t===1?1:(!r||r<1?(r=1,n=i/4):n=i*Math.asin(1/r)/(2*Math.PI),-(r*Math.pow(2,10*(t-=1))*Math.sin((t-n)*(2*Math.PI)/i)))},e.outElastic=function(t){var n,r=.1,i=.4;return t===0?0:t===1?1:(!r||r<1?(r=1,n=i/4):n=i*Math.asin(1/r)/(2*Math.PI),r*Math.pow(2,-10*t)*Math.sin((t-n)*(2*Math.PI)/i)+1)},e.inOutElastic=function(t){var n,r=.1,i=.4;return t===0?0:t===1?1:(!r||r<1?(r=1,n=i/4):n=i*Math.asin(1/r)/(2*Math.PI),(t*=2)<1?-.5*(r*Math.pow(2,10*(t-=1))*Math.sin((t-n)*(2*Math.PI)/i)):r*Math.pow(2,-10*(t-=1))*Math.sin((t-n)*(2*Math.PI)/i)*.5+1)},e["in-quad"]=e.inQuad,e["out-quad"]=e.outQuad,e["in-out-quad"]=e.inOutQuad,e["in-cube"]=e.inCube,e["out-cube"]=e.outCube,e["in-out-cube"]=e.inOutCube,e["in-quart"]=e.inQuart,e["out-quart"]=e.outQuart,e["in-out-quart"]=e.inOutQuart,e["in-quint"]=e.inQuint,e["out-quint"]=e.outQuint,e["in-out-quint"]=e.inOutQuint,e["in-sine"]=e.inSine,e["out-sine"]=e.outSine,e["in-out-sine"]=e.inOutSine,e["in-expo"]=e.inExpo,e["out-expo"]=e.outExpo,e["in-out-expo"]=e.inOutExpo,e["in-circ"]=e.inCirc,e["out-circ"]=e.outCirc,e["in-out-circ"]=e.inOutCirc,e["in-back"]=e.inBack,e["out-back"]=e.outBack,e["in-out-back"]=e.inOutBack,e["in-bounce"]=e.inBounce,e["out-bounce"]=e.outBounce,e["in-out-bounce"]=e.inOutBounce,e["in-elastic"]=e.inElastic,e["out-elastic"]=e.outElastic,e["in-out-elastic"]=e.inOutElastic})(Mf);var $f={exports:{}};(function(e){function t(r){if(r)return n(r)}function n(r){for(var i in t.prototype)r[i]=t.prototype[i];return r}t.prototype.on=t.prototype.addEventListener=function(r,i){return this._callbacks=this._callbacks||{},(this._callbacks["$"+r]=this._callbacks["$"+r]||[]).push(i),this},t.prototype.once=function(r,i){function a(){this.off(r,a),i.apply(this,arguments)}return a.fn=i,this.on(r,a),this},t.prototype.off=t.prototype.removeListener=t.prototype.removeAllListeners=t.prototype.removeEventListener=function(r,i){if(this._callbacks=this._callbacks||{},arguments.length==0)return this._callbacks={},this;var a=this._callbacks["$"+r];if(!a)return this;if(arguments.length==1)return delete this._callbacks["$"+r],this;for(var o,s=0;s=e;if(r)return this._from=this._to,this._update(this._to),this._done=!0,this.emit("end"),this;var i=this._from,a=this._to,o=this._curr,s=this._ease,l=(t-this._start)/e,c=s(l);if(this.isArray){for(var u=0;u"u"?ta:window,ni=["moz","webkit"],Mn="AnimationFrame",Fn=Ct["request"+Mn],Sr=Ct["cancel"+Mn]||Ct["cancelRequest"+Mn];for(var er=0;!Fn&&er{t.name==="index"&&(r.preventDefault(),Lf("body",{offset:0,duration:300}))};return(r,i)=>{const a=Wc("RouterLink"),o=Uo;return te(),ke(o,{enterFromClass:"scale-95 sm:scale-100 sm:!w-0 opacity-0"},{default:_e(()=>[pi(Z("div",y0,[ee(a,{class:"inline-block rotate-[-4deg] font-pacifico text-lg text-black-900/80 transition-colors duration-200 dark:text-white/80",to:{name:"index"},onClick:n},{default:_e(()=>[w0]),_:1})],512),[[bi,e.show]])]),_:1})}}}),_a=Ce(null);function Rf(){const e=Ir(),t=Ce(null);return Xt(()=>{je(e,async()=>{Yn&&(await Qt(),_a.value||(t.value=null))},{immediate:!0})}),Z1(_a,([{isIntersecting:n}])=>{t.value=n}),{coverRef:_a,isCoverVisible:t}}const Wo="about",x0=`#${Wo}`,to=`#${Wo}`;function Nf(e){Lf(x0,{offset:-64,duration:300,...e})}function E0(){const[e,t]=hf(),n=ce(()=>{if(Yn)return document.querySelector("body")}),r=rb(n);je(e,a=>{r.value=a});const{width:i}=db();return je(i,()=>{e.value=!1}),{isExpanded:e,toggleIsExpanded:t}}function C0(){const{y:e}=fb();return{isScrolledToTop:ce(()=>!e.value)}}const O0={class:"container flex px-4"},S0={class:"relative z-10 flex w-full items-center justify-center py-2 sm:justify-start"},A0={class:"mr-6 hidden space-x-6 font-medium transition-colors duration-75 sm:flex"},T0={class:"absolute left-0 top-1/2 block -translate-y-1/2 sm:static sm:right-auto sm:top-auto sm:hidden sm:transform-none"},P0={class:"absolute right-0 top-1/2 ml-auto -translate-y-1/2 sm:static sm:right-auto sm:top-auto sm:transform-none"},I0=he({__name:"TheNavbar",setup(e){const t=Ir(),{isCoverVisible:n}=Rf(),r=ce(()=>n.value==null?t.name!=="index"||t.name==="index"&&t.hash===to:!n.value),i=[{name:"About",href:{name:"index",hash:to},handler:c=>{t.name==="index"&&(c.preventDefault(),Nf())}},{name:"Blog",href:{name:"posts"}},{name:"Projects",href:{name:"projects"}},{name:"Notes",href:bt.hackmd.url},{name:"Gist",href:bt.githubGist.url}],{isExpanded:a,toggleIsExpanded:o}=E0(),{isScrolledToTop:s}=C0(),l=ce(()=>r.value&&!s.value);return(c,u)=>{const f=k0,d=xf,g=n0,m=t0,y=vb;return te(),Oe("nav",{class:yt(["fixed left-0 top-0 z-20 flex h-14 w-full flex-col justify-center border-b transition-all duration-300 after:absolute after:-bottom-8 after:left-0 after:h-8 after:w-full after:bg-gradient-to-b after:from-white after:to-[transparent_70%] after:opacity-0 dark:after:from-black-900 print:hidden sm:h-16",{"bg-white text-black-900/80 dark:bg-black-900 dark:text-white/80":ae(r),"text-white/80":!ae(r),"border-black-200 dark:border-black-800":ae(l),"border-transparent":!ae(l)}])},[Z("div",O0,[Z("div",S0,[ee(f,{show:ae(r),onClick:u[0]||(u[0]=k=>a.value=!1)},null,8,["show"]),Z("ul",A0,[(te(),Oe(Le,null,Jt(i,({name:k,href:h,handler:v},O)=>Z("li",{key:O},[ee(d,{href:h,onClick:S=>(v==null?void 0:v(S))??(()=>{})},{default:_e(()=>[Re(Xe(k),1)]),_:2},1032,["href","onClick"])])),64))]),Z("div",T0,[ee(g,{onClick:u[1]||(u[1]=k=>ae(o)())}),ee(m,{show:ae(a),"onUpdate:show":u[2]||(u[2]=k=>Be(a)?a.value=k:null),links:i},null,8,["show"])]),Z("div",P0,[ee(y)])])])],2)}}}),Zl="fe";function Df(){const e="fe",t=Gi(),n=Ir(),r=eb(e,!1);function i(){const a={...n.query};delete a[Zl],t.push({query:a})}return je(()=>Zl in n.query,a=>{a&&(r.value=!0,i())},{immediate:!0}),je(r,a=>{a||i()}),{isDownplayed:r}}function M0(){Xt(async()=>{var e;Yn&&(await new Promise(t=>setTimeout(t)),(e=document.querySelector("body"))==null||e.classList.add("transition-colors","duration-500"))})}var jf={exports:{}};/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT */(function(e,t){(function(n,r){e.exports=r()})(ta,function(){var n={};n.version="0.2.0";var r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};n.configure=function(m){var y,k;for(y in m)k=m[y],k!==void 0&&m.hasOwnProperty(y)&&(r[y]=k);return this},n.status=null,n.set=function(m){var y=n.isStarted();m=i(m,r.minimum,1),n.status=m===1?null:m;var k=n.render(!y),h=k.querySelector(r.barSelector),v=r.speed,O=r.easing;return k.offsetWidth,s(function(S){r.positionUsing===""&&(r.positionUsing=n.getPositioningCSS()),l(h,o(m,v,O)),m===1?(l(k,{transition:"none",opacity:1}),k.offsetWidth,setTimeout(function(){l(k,{transition:"all "+v+"ms linear",opacity:0}),setTimeout(function(){n.remove(),S()},v)},v)):setTimeout(S,v)}),this},n.isStarted=function(){return typeof n.status=="number"},n.start=function(){n.status||n.set(0);var m=function(){setTimeout(function(){n.status&&(n.trickle(),m())},r.trickleSpeed)};return r.trickle&&m(),this},n.done=function(m){return!m&&!n.status?this:n.inc(.3+.5*Math.random()).set(1)},n.inc=function(m){var y=n.status;return y?(typeof m!="number"&&(m=(1-y)*i(Math.random()*y,.1,.95)),y=i(y+m,0,.994),n.set(y)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},function(){var m=0,y=0;n.promise=function(k){return!k||k.state()==="resolved"?this:(y===0&&n.start(),m++,y++,k.always(function(){y--,y===0?(m=0,n.done()):n.set((m-y)/m)}),this)}}(),n.render=function(m){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var y=document.createElement("div");y.id="nprogress",y.innerHTML=r.template;var k=y.querySelector(r.barSelector),h=m?"-100":a(n.status||0),v=document.querySelector(r.parent),O;return l(k,{transition:"all 0 linear",transform:"translate3d("+h+"%,0,0)"}),r.showSpinner||(O=y.querySelector(r.spinnerSelector),O&&g(O)),v!=document.body&&u(v,"nprogress-custom-parent"),v.appendChild(y),y},n.remove=function(){f(document.documentElement,"nprogress-busy"),f(document.querySelector(r.parent),"nprogress-custom-parent");var m=document.getElementById("nprogress");m&&g(m)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var m=document.body.style,y="WebkitTransform"in m?"Webkit":"MozTransform"in m?"Moz":"msTransform"in m?"ms":"OTransform"in m?"O":"";return y+"Perspective"in m?"translate3d":y+"Transform"in m?"translate":"margin"};function i(m,y,k){return mk?k:m}function a(m){return(-1+m)*100}function o(m,y,k){var h;return r.positionUsing==="translate3d"?h={transform:"translate3d("+a(m)+"%,0,0)"}:r.positionUsing==="translate"?h={transform:"translate("+a(m)+"%,0)"}:h={"margin-left":a(m)+"%"},h.transition="all "+y+"ms "+k,h}var s=function(){var m=[];function y(){var k=m.shift();k&&k(y)}return function(k){m.push(k),m.length==1&&y()}}(),l=function(){var m=["Webkit","O","Moz","ms"],y={};function k(S){return S.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function($,I){return I.toUpperCase()})}function h(S){var $=document.body.style;if(S in $)return S;for(var I=m.length,w=S.charAt(0).toUpperCase()+S.slice(1),_;I--;)if(_=m[I]+w,_ in $)return _;return S}function v(S){return S=k(S),y[S]||(y[S]=h(S))}function O(S,$,I){$=v($),S.style[$]=I}return function(S,$){var I=arguments,w,_;if(I.length==2)for(w in $)_=$[w],_!==void 0&&$.hasOwnProperty(w)&&O(S,w,_);else O(S,I[1],I[2])}}();function c(m,y){var k=typeof m=="string"?m:d(m);return k.indexOf(" "+y+" ")>=0}function u(m,y){var k=d(m),h=k+y;c(k,y)||(m.className=h.substring(1))}function f(m,y){var k=d(m),h;c(m,y)&&(h=k.replace(" "+y+" "," "),m.className=h.substring(1,h.length-1))}function d(m){return(" "+(m.className||"")+" ").replace(/\s+/gi," ")}function g(m){m&&m.parentNode&&m.parentNode.removeChild(m)}return n})})(jf);var $0=jf.exports;const ec=Vo($0);function L0(){const{beforeEach:e,afterEach:t}=Gi();e((n,r,i)=>{ec.configure({showSpinner:!1,trickleSpeed:100}).start(),i()}),t(()=>setTimeout(ec.done,200))}const R0=he({__name:"App",setup(e){return M0(),L0(),Df(),(t,n)=>{const r=I0,i=Wc("RouterView"),a=m1;return te(),Oe("div",null,[ee(r),ee(i),ee(a)])}}}),N0="modulepreload",D0=function(e){return"/"+e},tc={},pe=function(t,n,r){if(!n||n.length===0)return t();const i=document.getElementsByTagName("link");return Promise.all(n.map(a=>{if(a=D0(a),a in tc)return;tc[a]=!0;const o=a.endsWith(".css"),s=o?'[rel="stylesheet"]':"";if(!!r)for(let u=i.length-1;u>=0;u--){const f=i[u];if(f.href===a&&(!o||f.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${a}"]${s}`))return;const c=document.createElement("link");if(c.rel=o?"stylesheet":N0,o||(c.as="script",c.crossOrigin=""),c.href=a,document.head.appendChild(c),o)return new Promise((u,f)=>{c.addEventListener("load",u),c.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${a}`)))})})).then(()=>t()).catch(a=>{const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a})},j0={class:"space-y-6"},F0={key:0,class:"text-4xl font-semibold"},qn=he({__name:"AboutMeSectionLayout",props:{title:null},setup(e){return(t,n)=>(te(),Oe("section",j0,[e.title?(te(),Oe("h2",F0,Xe(e.title),1)):fn("",!0),Zt(t.$slots,"default")]))}}),H0=Re(" View More "),Ff=he({__name:"LinkViewMore",setup(e){return(t,n)=>{const r=Bo;return te(),ke(r,null,{default:_e(()=>[H0,ee(ae(Pt),{class:"ml-1",icon:ae(n1)},null,8,["icon"])]),_:1})}}}),B0={class:"text-lg font-medium"},z0={class:"text-base text-black-700 dark:text-black-300"},U0=he({__name:"LinkPost",props:{title:null,date:null},setup(e){return(t,n)=>{const r=Mr;return te(),ke(r,{class:"relative inline-block w-full space-y-1 rounded-lg after:pointer-events-none after:absolute after:-inset-x-3 after:-inset-y-2 after:scale-[97%] after:rounded-xl after:bg-black-400/10 after:opacity-0 after:transition-all after:duration-200 hover:after:scale-100 hover:after:opacity-100"},{default:_e(()=>[Z("div",B0,Xe(e.title),1),Z("div",z0,Xe(e.date),1)]),_:1})}}});var Hf={exports:{}};(function(e,t){(function(n,r){e.exports=r()})(ta,function(){var n=1e3,r=6e4,i=36e5,a="millisecond",o="second",s="minute",l="hour",c="day",u="week",f="month",d="quarter",g="year",m="date",y="Invalid Date",k=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,h=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,v={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},O=function(N,C,D){var z=String(N);return!z||z.length>=C?N:""+Array(C+1-z.length).join(D)+N},S={s:O,z:function(N){var C=-N.utcOffset(),D=Math.abs(C),z=Math.floor(D/60),L=D%60;return(C<=0?"+":"-")+O(z,2,"0")+":"+O(L,2,"0")},m:function N(C,D){if(C.date()1)return N(J[0])}else{var de=C.name;I[de]=C,L=de}return!z&&L&&($=L),L||!z&&$},x=function(N,C){if(w(N))return N.clone();var D=typeof C=="object"?C:{};return D.date=N,D.args=arguments,new Y(D)},T=S;T.l=_,T.i=w,T.w=function(N,C){return x(N,{locale:C.$L,utc:C.$u,x:C.$x,$offset:C.$offset})};var Y=function(){function N(D){this.$L=_(D.locale,null,!0),this.parse(D)}var C=N.prototype;return C.parse=function(D){this.$d=function(z){var L=z.date,V=z.utc;if(L===null)return new Date(NaN);if(T.u(L))return new Date;if(L instanceof Date)return new Date(L);if(typeof L=="string"&&!/Z$/i.test(L)){var J=L.match(k);if(J){var de=J[2]-1||0,we=(J[7]||"0").substring(0,3);return V?new Date(Date.UTC(J[1],de,J[3]||1,J[4]||0,J[5]||0,J[6]||0,we)):new Date(J[1],de,J[3]||1,J[4]||0,J[5]||0,J[6]||0,we)}}return new Date(L)}(D),this.$x=D.x||{},this.init()},C.init=function(){var D=this.$d;this.$y=D.getFullYear(),this.$M=D.getMonth(),this.$D=D.getDate(),this.$W=D.getDay(),this.$H=D.getHours(),this.$m=D.getMinutes(),this.$s=D.getSeconds(),this.$ms=D.getMilliseconds()},C.$utils=function(){return T},C.isValid=function(){return this.$d.toString()!==y},C.isSame=function(D,z){var L=x(D);return this.startOf(z)<=L&&L<=this.endOf(z)},C.isAfter=function(D,z){return x(D)Nr(e).format("MMM, YYYY"),nc=e=>Nr(e).format("YYYY"),W0=e=>Nr(e).format("MMM DD, YYYY"),Y0=he({__name:"PostList",props:{list:null,dense:{type:Boolean}},setup(e){return(t,n)=>{const r=U0;return te(),Oe("ul",{class:yt([e.dense?"space-y-5":"space-y-8"])},[(te(!0),Oe(Le,null,Jt(e.list,(i,a)=>(te(),Oe("li",{key:a},[ee(r,{date:ae(W0)(i.date),href:i.path,title:i.title},null,8,["date","href","title"])]))),128))],2)}}}),rc=e=>+Nr(e);function K0(){const e=Gi(),t=ce(()=>e.getRoutes().filter(a=>/\/blog\/(.+)/.test(a.path)).filter(a=>!a.redirect).map(a=>({...a.meta.frontmatter,path:a.path})).filter(a=>!a.draft).sort((a,o)=>rc(o.date)-rc(a.date))),n=ce(()=>{const r=t.value.reduce((i,a)=>{const o=Nr(a.date).year(),s=i[o]??{name:o,posts:[]};return s.posts.push(a),i[o]=s,i},{});return Object.keys(r).sort((i,a)=>Number(a)-Number(i)).map(i=>r[i])});return{posts:t,postGroups:n}}const q0={class:"space-y-8 pt-4"},G0=he({__name:"AboutMeSectionPosts",setup(e){const{posts:t}=K0(),n=ce(()=>t.value.slice(0,2));return(r,i)=>{const a=Y0,o=Ff,s=qn;return te(),ke(s,{title:"Recent Posts"},{default:_e(()=>[Z("div",q0,[ee(a,{dense:"",list:ae(n)},null,8,["list"]),Z("div",null,[ee(o,{href:{name:"posts"}})])])]),_:1})}}}),Q0=he({__name:"LinkIconDemo",setup(e){return(t,n)=>{const r=Ho;return te(),ke(r,{icon:ae(ff),title:"前往 Demo 頁面",onClick:n[0]||(n[0]=fu(()=>{},["stop"]))},null,8,["icon"])}}}),X0=he({__name:"LinkIconGithub",setup(e){return(t,n)=>{const r=Ho;return te(),ke(r,{icon:ae(li),title:"前往 GitHub 頁面",onClick:n[0]||(n[0]=fu(()=>{},["stop"]))},null,8,["icon"])}}}),J0=["src"],Z0=he({__name:"CardProjectImageGlow",props:{img:{default:void 0},sizeClassName:{default:"h-[5.5rem] w-[8rem]"}},setup(e){const t=Ce(!1);async function n(){t.value=!0}return(r,i)=>(te(),Oe("div",{class:yt(["relative before:absolute before:left-0 before:top-0 before:-z-10 before:h-full before:w-full before:rounded-xl before:bg-black-100 after:absolute after:left-0 after:top-0 after:-z-20 after:h-full after:w-full after:overflow-hidden after:rounded-xl after:bg-cover after:bg-center after:blur-lg after:transition-opacity after:duration-500 after:[background-image:var(--img)] after:[will-change:filter] dark:before:bg-black-800",{"after:opacity-0":!t.value,"after:opacity-40":t.value,[e.sizeClassName]:!0}]),style:Hn({"--img":`url(${e.img})`})},[Z("img",{class:yt(["h-full w-full overflow-hidden rounded-xl bg-black-200 object-cover transition-opacity duration-300 dark:bg-black-800",{"opacity-0":!t.value}]),src:e.img,onLoad:n},null,42,J0)],6))}}),e_={};function t_(e,t){const n=Mr;return te(),ke(n,{class:"absolute inset-0 after:pointer-events-none after:absolute after:-inset-3 after:scale-[97%] after:rounded-xl after:bg-black-400/10 after:opacity-0 after:transition-all after:duration-200 hover:after:scale-100 hover:after:opacity-100"})}const n_=Wn(e_,[["render",t_]]),r_={class:"relative flex"},i_={class:"pointer-events-none z-10 mr-5 flex-none"},a_={class:"flex flex-1 flex-col justify-center"},o_={class:"pointer-events-none z-10"},s_={class:"!mb-2 !mt-0 !text-lg !font-medium !leading-5"},l_={class:"!m-0 !text-sm !leading-4 text-black-700 dark:text-black-300"},c_={key:0,class:"mt-3 space-x-2 leading-none"},u_=he({__name:"CardProject",props:{path:null,img:null,title:null,description:null,github:null,link:null},setup(e){return(t,n)=>{const r=n_,i=Z0,a=X0,o=Q0;return te(),Oe("div",r_,[e.path?(te(),ke(r,{key:0,href:e.path},null,8,["href"])):fn("",!0),Z("div",i_,[ee(i,{img:e.img},null,8,["img"])]),Z("div",a_,[Z("div",o_,[Z("h3",s_,Xe(e.title),1),Z("p",l_,Xe(e.description),1)]),e.github||e.link?(te(),Oe("div",c_,[e.github?(te(),ke(a,{key:0,href:e.github},null,8,["href"])):fn("",!0),e.link?(te(),ke(o,{key:1,href:e.link},null,8,["href"])):fn("",!0)])):fn("",!0)])])}}}),f_={class:"grid !list-none grid-cols-1 gap-8 !p-0 md:grid-cols-2"},d_=he({__name:"ProjectList",props:{list:null},setup(e){return(t,n)=>{const r=u_;return te(),Oe("ul",f_,[(te(!0),Oe(Le,null,Jt(e.list,({path:i,title:a,briefDescription:o,githubLink:s,demoLink:l,cover:c},u)=>(te(),Oe("li",{key:u,class:"!p-0"},[ee(r,{description:o,github:s,img:c,link:l,path:i,title:a},null,8,["description","github","img","link","path","title"])]))),128))])}}});function p_(){const e=Gi(),{isDownplayed:t}=Df(),n=ce(()=>e.getRoutes().filter(o=>/\/project\/(.+)/.test(o.path)).filter(o=>!o.redirect).reduce((o,s)=>{var u;const l=(u=s.name)==null?void 0:u.split("project-")[1],c=s.meta.frontmatter;return o[l]={...c,path:s.path,cover:`${c.cover}?v=b795911 +`},o},{})),r=ce(()=>{const a=n.value;return[{title:"Browser Extension",list:[{title:"Taiwan Company Blocker",briefDescription:"台灣求職網封鎖神器",githubLink:"https://github.com/ngseke/taiwan-company-blocker/",demoLink:"https://chromewebstore.google.com/detail/%E5%8F%B0%E7%81%A3%E6%B1%82%E8%81%B7%E7%B6%B2%E5%B0%81%E9%8E%96%E7%A5%9E%E5%99%A8/hmkkfmjfinbllbbkgabkeponkhckmijk",cover:"/img/project-cover/taiwan-company-blocker.png"},a["versatile-npm"],a["leetcode-night"]]},{title:"Web",list:[a["iphone-price"],a.koasu,a.mcip,a["mcip-cms"],a["credit-card-calc"],a["em-optimization-lab"]]},{title:"System Design",list:[a.boss]},{title:"Game",list:[{title:"Flip Card",briefDescription:"Emoji 翻牌配對遊戲",githubLink:"https://github.com/ngseke/vue-flip-card",demoLink:"https://ngseke.github.io/vue-flip-card/",cover:"/img/project-cover/flip-card.png"},a.gomoku,a["tic-tac-toe"],a["raise-your-red-flag"],a["typing-typing"]]},{title:"Identity Design",list:[a["camp-2017"],a.shanlinliang]}]}),i=ce(()=>r.value.filter(a=>t.value?a.title!=="Identity Design":!0));return{projectMap:n,projects:r,downplayedProjects:i}}const m_={class:"space-y-8 pt-4"},h_={class:"pl-1"},g_=he({__name:"AboutMeSectionProjects",setup(e){const{projectMap:t}=p_(),n=[t.value["versatile-npm"],t.value["leetcode-night"]];return(r,i)=>{const a=d_,o=Ff,s=qn;return te(),ke(s,{title:"Projects"},{default:_e(()=>[Z("div",m_,[ee(a,{list:n}),Z("div",h_,[ee(o,{href:{name:"projects"}})])])]),_:1})}}}),v_={class:"flex flex-col"},b_={class:"mr-1 inline-block min-w-[1.5rem] text-center"},__=he({__name:"AboutMeSectionContactMe",setup(e){const t=[bt.github,bt.discord,bt.email,bt.hackmd,bt.linkedin];return(n,r)=>{const i=Bo,a=qn;return te(),ke(a,{title:"Contact Me"},{default:_e(()=>[Z("ul",v_,[(te(),Oe(Le,null,Jt(t,({title:o,url:s,icon:l},c)=>Z("li",{key:c},[ee(i,{href:s},{default:_e(()=>[Z("span",b_,[ee(ae(Pt),{icon:l},null,8,["icon"])]),Re(" "+Xe(o),1)]),_:2},1032,["href"])])),64))])]),_:1})}}}),y_={class:"flex max-w-md flex-col space-y-3 pt-2 leading-tight"},w_={class:"ml-2"},k_={class:"ml-1 mr-3 font-medium"},x_=Z("br",{class:"inline-block sm:hidden"},null,-1),E_={class:"whitespace-nowrap text-base text-black-700 dark:text-black-300"},C_=he({__name:"AboutMeSectionExperience",setup(e){const t=i=>({"flex flex-1 before:mr-1 before:min-w-[.5rem] before:text-center":!0,'opacity-60 before:content-["•"]':i,'before:content-["▸"]':!i}),n=[{title:"Software Engineer",company:"ASUS AICS",period:["2022/07","2023/05"]},{title:"Software Engineer",company:"3drens",period:["2021/08","2022/03"]},{title:"Software Engineer",company:"gogoout",period:["2019/12","2021/07"]},{title:"Summer Engineering Intern",company:"Hiero7",period:"Summer 2018"}],r=i=>{if(typeof i=="string")return i;const a=nc(i[0]),o=i[1]?nc(i[1]):"Present";return`${a} — ${o}`};return(i,a)=>{const o=qn;return te(),ke(o,{title:"Experience"},{default:_e(()=>[Z("ul",y_,[(te(),Oe(Le,null,Jt(n,({title:s,company:l,period:c},u)=>Z("li",{key:u,class:yt(t(u))},[Z("div",w_,[Z("span",null,Xe(s),1),Z("span",k_," @ "+Xe(l),1),x_,Z("span",E_,Xe(r(c)),1)])],2)),64))])]),_:1})}}}),O_={class:"flex max-w-md flex-wrap"},S_={class:"ml-2"},A_=he({__name:"AboutMeSectionSkills",setup(e){const t=["React","Vue 3","Tailwind CSS","Nuxt","TypeScript","Node.js"];return(n,r)=>{const i=qn;return te(),ke(i,{title:"Skills"},{default:_e(()=>[Z("ul",O_,[(te(),Oe(Le,null,Jt(t,(a,o)=>Z("li",{key:o,class:"min-w-[50%] flex-1 before:mr-1 before:inline-block before:origin-right before:opacity-50 before:content-['▸']"},[Z("span",S_,Xe(a),1)])),64))])]),_:1})}}}),T_={},P_={class:"relative not-italic after:absolute after:inset-x-[-1px] after:bottom-[10%] after:h-[20%] after:bg-ngsek/60 dark:after:bg-ngsek/30"},I_={class:"relative z-10"};function M_(e,t){return te(),Oe("em",P_,[Z("span",I_,[Zt(e.$slots,"default")])])}const $_=Wn(T_,[["render",M_]]),L_={},R_={class:"flex flex-col space-y-2"},N_=Z("span",{class:"font-mono text-black-700 dark:text-black-400"}," @ngseke ",-1),D_={class:"text-5xl font-bold sm:text-6xl md:text-7xl"},j_=Re(" Hi, I'm "),F_={class:"whitespace-nowrap"},H_=Re(" Sean"),B_=Re(". 🍻 "),z_=Re(" 現職前端軟體工程師,專注於 "),U_=Re("TypeScript"),V_=Re("、"),W_=Re("Vue"),Y_=Re(" 和 "),K_=Re("React"),q_=Re("。 "),G_=Z("br",null,null,-1),Q_=Re(" 追求撰寫無瑕程式碼是我的開發格言,並致力於優化開發者體驗。 "),X_=Z("br",null,null,-1),J_=Re(" 熱衷探究前端領域的新鮮事,期待藉由知識分享為社群創造影響力。 "),Z_=Z("p",null,[Re(" aka "),Z("span",{class:"underline decoration-dotted",title:"ㄏㄨㄤˊ ㄒㄧㄥˇ ㄑㄧㄠˊ"},"黃省喬"),Re(" / HUANG Sing-Ciao ")],-1);function ey(e,t){const n=$_,r=qn;return te(),ke(r,null,{default:_e(()=>[Z("div",R_,[N_,Z("h2",D_,[j_,Z("span",F_,[ee(n,null,{default:_e(()=>[H_]),_:1}),B_])])]),Z("p",null,[z_,ee(n,null,{default:_e(()=>[U_]),_:1}),V_,ee(n,null,{default:_e(()=>[W_]),_:1}),Y_,ee(n,null,{default:_e(()=>[K_]),_:1}),q_,G_,Q_,X_,J_]),Z_]),_:1})}const ty=Wn(L_,[["render",ey]]),ny={},ry={class:"min-h-screen"},iy={class:"container flex justify-center px-4 py-16"},ay={class:"w-full max-w-3xl space-y-14 text-lg leading-loose"};function oy(e,t){const n=ty,r=A_,i=C_,a=__,o=g_,s=G0;return te(),Oe("div",ry,[Z("div",iy,[Z("article",ay,[ee(n),ee(r),ee(i),ee(a),ee(o),ee(s)])])])}const sy=Wn(ny,[["render",oy]]),ly=e=>(Bd("data-v-b1cdf682"),e=e(),zd(),e),cy={class:"relative flex h-screen w-full flex-col items-center justify-center bg-black-900/20 px-4 md:flex-row"},uy={class:"flex justify-center md:flex-1"},fy={class:"transition-opacity delay-200 duration-700"},dy=ly(()=>Z("h1",{class:"rotate-[-4deg] whitespace-nowrap font-pacifico text-8xl tracking-tight text-white"},[Z("span",{class:"neon blink relative z-10 inline-block after:absolute after:left-0 after:top-0 after:-z-10 after:text-transparent after:content-['ngseke']"}," ngseke ")],-1)),py=[dy],my=he({__name:"CoverBody",setup(e){const t=tb();return(n,r)=>{const i=Uo;return te(),Oe("div",cy,[Z("div",uy,[ee(i,{enterFromClass:"opacity-0"},{default:_e(()=>[pi(Z("div",fy,py,512),[[bi,ae(t)]])]),_:1})])])}}});const hy=Wn(my,[["__scopeId","data-v-b1cdf682"]]),gy="/assets/cover-background-f6446981.jpg",vy=he({__name:"Cover",setup(e){const t={backgroundImage:`url(${gy})`},{coverRef:n}=Rf();return(r,i)=>{const a=hy;return te(),Oe("header",{ref_key:"coverRef",ref:n,class:"flex min-h-screen flex-col justify-center bg-black-900 bg-cover bg-center",style:t},[Zt(r.$slots,"default",{},()=>[ee(a)])],512)}}}),ic="ngseke",s2="https://ngseke.me",l2="img/og",c2="Sean Huang",Bf=he({__name:"index",setup(e){const t=Ir();return Xt(async()=>{t.hash===to&&(await Qt(),Nf({duration:1}))}),eg({title:`${ic}`,meta:[{property:"og:site_name",content:ic}]}),(n,r)=>{const i=vy,a=sy;return te(),Oe(Le,null,[ee(i),ee(a,{id:ae(Wo)},null,8,["id"])],64)}}}),ac={};typeof ac=="function"&&ac(Bf);const by=()=>pe(()=>import("./projects-34cb3ecc.js"),["assets/projects-34cb3ecc.js","assets/useReadHistory-d98c4b5d.js"]),_y=()=>pe(()=>import("./project-e24afe95.js"),["assets/project-e24afe95.js","assets/useOgImage-203f60bd.js","assets/useOgImage-588433c9.css","assets/useReadHistory-d98c4b5d.js"]),yy=()=>pe(()=>import("./versatile-npm-d3f5c477.js"),[]),wy=()=>pe(()=>import("./typingtyping-b132256f.js"),[]),ky=()=>pe(()=>import("./typing-typing-876fd270.js"),[]),xy=()=>pe(()=>import("./tic-tac-toe-dd64aee5.js"),[]),Ey=()=>pe(()=>import("./shanlinliang-9edbff22.js"),[]),Cy=()=>pe(()=>import("./raise-your-red-flag-6df7832f.js"),[]),Oy=()=>pe(()=>import("./mcip-28b770e5.js"),[]),Sy=()=>pe(()=>import("./mcip-cms-dcfd5ebb.js"),[]),Ay=()=>pe(()=>import("./leetcode-night-b75823ff.js"),[]),Ty=()=>pe(()=>import("./koasu-5245e6bf.js"),[]),Py=()=>pe(()=>import("./iphone-price-c267c7d7.js"),[]),Iy=()=>pe(()=>import("./index-95e78139.js"),[]),My=()=>pe(()=>import("./gomoku-e312c04b.js"),[]),$y=()=>pe(()=>import("./flag-5557c288.js"),[]),Ly=()=>pe(()=>import("./emo-5557c288.js"),[]),Ry=()=>pe(()=>import("./em-optimization-lab-d89b3c86.js"),[]),Ny=()=>pe(()=>import("./credit-card-calc-28bb5562.js"),[]),Dy=()=>pe(()=>import("./camp2017-8a21aa39.js"),[]),jy=()=>pe(()=>import("./camp-2017-d632b91c.js"),[]),Fy=()=>pe(()=>import("./boss-7d1971fc.js"),[]),Hy=()=>pe(()=>import("./fe-e736b23f.js"),[]),By=()=>pe(()=>import("./blog-f75762ab.js"),["assets/blog-f75762ab.js","assets/useOgImage-203f60bd.js","assets/useOgImage-588433c9.css"]),zy=()=>pe(()=>import("./wikipedia-link-converter-5743973b.js"),[]),Uy=()=>pe(()=>import("./webpack-config-esm-320be40c.js"),[]),Vy=()=>pe(()=>import("./vite-vue-ts-eslint-setup-1bf5e98d.js"),[]),Wy=()=>pe(()=>import("./typescript-as-const-fb61f8e3.js"),[]),Yy=()=>pe(()=>import("./reproduce-bootstrap-grid-in-tailwind-ce693ef5.js"),[]),Ky=()=>pe(()=>import("./react-handler-type-3a8ff22d.js"),[]),qy=()=>pe(()=>import("./index-4cd242c9.js"),[]),Gy=()=>pe(()=>import("./customize-youtube-caption-font-3e0540da.js"),[]),Qy=()=>pe(()=>import("./component-naming-87122709.js"),[]),Xy=()=>pe(()=>import("./code-review-taxi-number-a7cd2111.js"),[]),Jy=()=>pe(()=>import("./check-if-key-exists-ce0584a7.js"),[]),Zy=()=>pe(()=>import("./bootstrap-in-nuxt-1bb6f56c.js"),[]),e2=()=>pe(()=>import("./about-c7f247d5.js"),[]),t2=()=>pe(()=>import("./404-c7f247d5.js"),[]),n2=()=>pe(()=>import("./_...404_-c7f247d5.js"),[]),r2=[{name:"projects",path:"/projects",component:by,props:!0},{path:"/project",component:_y,children:[{name:"project-versatile-npm",path:"versatile-npm",component:yy,props:!0,meta:{frontmatter:{title:"Versatile Npm",briefDescription:"自訂 Npm 安裝指令瀏覽器擴充功能",githubLink:"https://github.com/ngseke/versatile-npm",demoLink:"https://chromewebstore.google.com/detail/versatile-npm/jahejogdoffpehfhkhbpjblnlhghjnje?hl=zh-TW",period:["2023/11","2023/11"],cover:"/img/project-cover/versatile-npm.png",tags:["Vue","TypeScript","Vuetify","npm"],name:"project"}}},{name:"project-typingtyping",path:"typingtyping",component:wy,props:!0,redirect:{name:"project-typing-typing"}},{name:"project-typing-typing",path:"typing-typing",component:ky,props:!0,meta:{frontmatter:{title:"Typing Typing!",briefDescription:"8-bit 復古風格打字遊戲",githubLink:"https://github.com/ngseke/Typing-Typing",period:["2017/02","2017/06"],members:["余鎧企","黃省喬"],cover:"/img/project-cover/typing-typing.png",tags:["C++"],name:"project"}}},{name:"project-tic-tac-toe",path:"tic-tac-toe",component:xy,props:!0,meta:{frontmatter:{title:"Tic Tac Toe",briefDescription:"圈圈叉叉亂鬥",githubLink:"https://github.com/ngseke/tic-tac-toe",demoLink:"https://ngseke.github.io/tic-tac-toe/",period:["2018/10","2018/10"],cover:"/img/project-cover/tic-tac-toe.png",tags:["Vue","gulp","Firebase","UI/UX"],name:"project"}}},{name:"project-shanlinliang",path:"shanlinliang",component:Ey,props:!0,meta:{frontmatter:{title:"Shanlinliang",briefDescription:"虛構涼扇品牌廣告《扇林涼》",period:["2017/10","2017/10"],cover:"/img/project-cover/shanlinliang.png",tags:["Illustrator"],name:"project"}}},{name:"project-raise-your-red-flag",path:"raise-your-red-flag",component:Cy,props:!0,meta:{frontmatter:{title:"Raise Your Red Flag",briefDescription:"以 Webcam 重現經典團康遊戲《紅旗舉起來》",githubLink:"https://github.com/ngseke/Raise-Your-Red-Flag",demoLink:"https://raise-flag.web.app/",period:["2018/06","2018/06"],cover:"/img/project-cover/flag.png",name:"project"}}},{name:"project-mcip",path:"mcip",component:Oy,props:!0,meta:{frontmatter:{title:"MCIP Official Website",briefDescription:"《樂台計畫》官方網站",githubLink:"https://github.com/ngseke/mcip.ml",demoLink:"https://mcip.app",period:"2019/02",cover:"/img/project-cover/mcip.png",tags:["Nuxt","Schema.org","UI/UX"],name:"project"}}},{name:"project-mcip-cms",path:"mcip-cms",component:Sy,props:!0,meta:{frontmatter:{title:"MCIP CMS",briefDescription:"《樂台計畫》後台管理系統",period:"2018/11",cover:"/img/project-cover/mcip-cms.png",tags:["Vue","Vuetify","ECharts","UI/UX"],name:"project"}}},{name:"project-leetcode-night",path:"leetcode-night",component:Ay,props:!0,meta:{frontmatter:{title:"LeetCode Night",briefDescription:"LeetCode 深色模式瀏覽器擴充功能",githubLink:"https://github.com/ngseke/leetcode-night",demoLink:"https://chrome.google.com/webstore/detail/leetcode-night/aaokgipfeeeciodnffigjfiafledhcii",period:"2022/02",cover:"/img/project-cover/leetcode-night.png",tags:["React","TypeScript","i18n","styled-components","UI/UX"],name:"project"}}},{name:"project-koasu",path:"koasu",component:Ty,props:!0,meta:{frontmatter:{title:"Koasu",briefDescription:"KOASÛ 白話字台語歌詞網誌",githubLink:"https://github.com/ngseke/koasu",demoLink:"https://ngseke.github.io/koasu/",period:"2021/08",cover:"/img/project-cover/koasu.png",tags:["Hexo"],name:"project"}}},{name:"project-iphone-price",path:"iphone-price",component:Py,props:!0,meta:{frontmatter:{title:"iPhone Price",briefDescription:"台灣 iPhone 價格歷史趨勢",githubLink:"https://github.com/ngseke/iphone-price",demoLink:"https://iphone-price.ngseke.me/",period:["2023/10","2023/10"],cover:"/img/project-cover/iphone-price.png",tags:["Vue","TypeScript","Tailwind","daisyUI","ECharts","UI/UX"],name:"project"}}},{name:"project",path:"",component:Iy,props:!0,redirect:{name:"projects"}},{name:"project-gomoku",path:"gomoku",component:My,props:!0,meta:{frontmatter:{title:"Gomoku",briefDescription:"線上五子棋對戰",githubLink:"https://github.com/ngseke/gomoku",demoLink:"https://gomoku.ngseke.me",period:["2018/10","2018/11"],cover:"/img/project-cover/gomoku.png",tags:["Vue","Firebase","UI/UX"],name:"project"}}},{name:"project-flag",path:"flag",component:$y,props:!0,redirect:{name:"project-raise-your-red-flag"}},{name:"project-emo",path:"emo",component:Ly,props:!0,redirect:{name:"project-em-optimization-lab"}},{name:"project-em-optimization-lab",path:"em-optimization-lab",component:Ry,props:!0,meta:{frontmatter:{title:"EM Optimization Lab",briefDescription:"《電磁最佳化實驗室》網站",githubLink:"https://github.com/ngseke/emo",demoLink:"https://myweb.ntut.edu.tw/~yschen/",period:["2018/03","2018/10"],cover:"/img/project-cover/emo.png",tags:["Vue","Firebase","gulp.js","UI/UX"],name:"project"}}},{name:"project-credit-card-calc",path:"credit-card-calc",component:Ny,props:!0,meta:{frontmatter:{title:"Sinopac Dual Currency Card Calc.",briefDescription:"永豐幣倍卡回饋計算機",githubLink:"https://github.com/ngseke/sinopac-dual-currency-card-calculator",demoLink:"https://ngseke.github.io/sinopac-dual-currency-card-calculator/",period:["2021/06","2021/06"],cover:"/img/project-cover/credit-card-calc.png",tags:["Vue","TypeScript","Tailwind CSS","Vite","UI/UX"],name:"project"}}},{name:"project-camp2017",path:"camp2017",component:Dy,props:!0,redirect:{name:"project-camp-2017"}},{name:"project-camp-2017",path:"camp-2017",component:jy,props:!0,meta:{frontmatter:{title:"NTUT-CSIE & NTUB-ACC Camp, 2017",briefDescription:"迎新《會炒不加辣,果資不加糖》品牌識別設計",period:["2017/07","2017/08"],cover:"/img/project-cover/camp2017.png",tags:["Illustrator","Final Cut Pro"],name:"project"}}},{name:"project-boss",path:"boss",component:Fy,props:!0,meta:{frontmatter:{title:"BOSS: Beverage Online Shop System",briefDescription:"線上飲料購物系統",githubLink:"https://github.com/ngseke/boss",period:["2017/11","2018/01"],members:["吳品頤","余鎧企","黃省喬","趙振廷"],cover:"/img/project-cover/boss.png",tags:["PHP","MySQL","UI/UX"],name:"project"}}}],props:!0,name:"project"},{name:"index",path:"/",component:Bf,props:!0},{name:"fe",path:"/fe",component:Hy,props:!0},{name:"post",path:"/blog",component:By,children:[{name:"blog-wikipedia-link-converter",path:"wikipedia-link-converter",component:zy,props:!0,meta:{frontmatter:{title:"還給你 Google 搜尋結果的桌面版維基百科",date:"2022/05/27",tags:["Tampermonkey"],name:"blog"}}},{name:"blog-webpack-config-esm",path:"webpack-config-esm",component:Uy,props:!0,meta:{frontmatter:{title:"在 webpack.config.js 裡使用 ESM 的 import 語法",date:"2022/02/20",tags:["Webpack"],original:"https://hackmd.io/@xq/webpack-config-esm",name:"blog"}}},{name:"blog-vite-vue-ts-eslint-setup",path:"vite-vue-ts-eslint-setup",component:Vy,props:!0,meta:{frontmatter:{title:"建立 Vite + Vue + TypeScript + ESLint 專案可能會遇到的坑",date:"2023/10/03",tags:["Vite","Vue","TypeScript","ESLint"],original:"https://hackmd.io/@xq/vite-vue-ts-eslint-setup",name:"blog"}}},{name:"blog-typescript-as-const",path:"typescript-as-const",component:Wy,props:!0,meta:{frontmatter:{title:"讓 TypeScript 的 as const 救你一命",date:"2021/12/03",tags:["TypeScript"],original:"https://hackmd.io/@xq/as-const",name:"blog"}}},{name:"blog-reproduce-bootstrap-grid-in-tailwind",path:"reproduce-bootstrap-grid-in-tailwind",component:Yy,props:!0,meta:{frontmatter:{title:"用 Tailwind 重現 Bootstrap 的 Grid System",date:"2022/08/22",tags:["Tailwind","Bootstrap"],name:"blog"}}},{name:"blog-react-handler-type",path:"react-handler-type",component:Ky,props:!0,meta:{frontmatter:{title:"處理在 React 抽出 event handler 時常碰到的 TypeScript 參數型別問題",date:"2022/06/06",tags:["React","TypeScript"],name:"blog"}}},{name:"posts",path:"",component:qy,props:!0},{name:"blog-customize-youtube-caption-font",path:"customize-youtube-caption-font",component:Gy,props:!0,meta:{frontmatter:{title:"把 YouTube CC 字幕變成粉圓體",date:"2022/08/14",tags:["Tampermonkey"],name:"blog"}}},{name:"blog-component-naming",path:"component-naming",component:Qy,props:!0,meta:{frontmatter:{title:"試著用有點違反直覺的方式命名組件",date:"2021/11/28",tags:["Vue","React"],original:"https://hackmd.io/@xq/component-naming-reversely",name:"blog"}}},{name:"blog-code-review-taxi-number",path:"code-review-taxi-number",component:Xy,props:!0,meta:{frontmatter:{title:"如果太認真去 Code Review 會發生什麼事?",date:"2023/05/31",tags:["JavaScript"],name:"blog"}}},{name:"blog-check-if-key-exists",path:"check-if-key-exists",component:Jy,props:!0,meta:{frontmatter:{title:"檢查物件的 key 是否存在的 N 種方法",date:"2023/07/07",tags:["JavaScript"],original:"https://hackmd.io/@xq/check-if-key-exist",name:"blog"}}},{name:"blog-bootstrap-in-nuxt",path:"bootstrap-in-nuxt",component:Zy,props:!0,meta:{frontmatter:{title:"在 Nuxt 專案穩穩地匯入 Bootstrap",date:"2021/11/21",tags:["Nuxt","Vue","Bootstrap"],original:"https://hackmd.io/@xq/bootstrap-with-nuxt",name:"blog"}}}],props:!0},{name:"about",path:"/about",component:e2,props:!0,redirect:{name:"index",hash:"#about"}},{name:"404",path:"/404",component:t2,props:!0,redirect:{name:"index"}},{name:"404",path:"/:404(.*)*",component:n2,props:!0,redirect:{name:"index"}}];ag(R0,{routes:r2,scrollBehavior(e,t,n){if(![e.name,t.name].every(r=>r==="projects"))return n||{top:0}}});export{Bd as $,a2 as A,Gi as B,Xt as C,W0 as D,Pt as E,Le as F,n1 as G,c2 as H,Yn as I,vo as J,je as K,Qt as L,mb as M,ab as N,yt as O,ri as P,Ce as Q,ft as R,i2 as S,Yi as T,Vc as U,ic as V,pi as W,Pr as X,Op as Y,Z0 as Z,Wn as _,Oe as a,zd as a0,s2 as a1,l2 as a2,K0 as a3,Y0 as a4,fn as b,ke as c,he as d,eg as e,p_ as f,Df as g,Z as h,Jt as i,ee as j,Re as k,d_ as l,ac as m,Mr as n,te as o,Ir as p,ce as q,Zt as r,o2 as s,Xe as t,ae as u,Wc as v,_e as w,X0 as x,Q0 as y,eb as z}; diff --git a/assets/banner-63e3ec60.png b/assets/banner-63e3ec60.png new file mode 100644 index 00000000..e49bb173 Binary files /dev/null and b/assets/banner-63e3ec60.png differ diff --git a/assets/before-485cea7e.jpg b/assets/before-485cea7e.jpg new file mode 100644 index 00000000..851fb2c4 Binary files /dev/null and b/assets/before-485cea7e.jpg differ diff --git a/assets/before-7a436972.png b/assets/before-7a436972.png new file mode 100644 index 00000000..c7303f46 Binary files /dev/null and b/assets/before-7a436972.png differ diff --git a/assets/blog-f75762ab.js b/assets/blog-f75762ab.js new file mode 100644 index 00000000..34bf6392 --- /dev/null +++ b/assets/blog-f75762ab.js @@ -0,0 +1 @@ +import{u as O,_ as $,b as A}from"./useOgImage-203f60bd.js";import{d as I,o as s,a as r,F as R,i as D,t as u,b as f,p as E,q as c,V as w,e as S,D as j,v as T,u as t,c as p,w as g,j as i,h as o,E as M,G as q,k as G,H as U,m as V}from"./app-033a95d9.js";const z={key:0,class:"mb-2 flex space-x-2 text-lg font-semibold text-ngsek"},J=I({__name:"PostTags",props:{list:null},setup(l){return(d,e)=>l.list?(s(),r("ul",z,[(s(!0),r(R,null,D(l.list,(n,_)=>(s(),r("li",{key:_,class:"before:content-['#']"},u(n),1))),128))])):f("",!0)}});function K(){const l=E();return{frontmatter:c(()=>{var e;return(e=l.meta)==null?void 0:e.frontmatter})}}const Q={class:"mb-6 text-3xl font-semibold md:text-4xl md:leading-tight"},W={class:"flex space-x-1 text-sm text-black-700 dark:text-black-300"},X=o("span",{class:'opacity-60 before:content-["•"]'},null,-1),Y={key:3,class:"container px-4 pb-8"},Z={class:"mx-auto max-w-3xl"},tt=o("hr",{class:"mb-8 border-dashed border-black-200 dark:border-black-800"},null,-1),et={class:"flex flex-wrap"},ot=G(" 閱讀更多文章 "),st=I({__name:"blog",setup(l){const d=E(),{frontmatter:e}=K(),n=c(()=>{var a;return`${((a=e.value)==null?void 0:a.title)??"Blog"} | ${w}`}),{ogImageHeadMetaList:_,shouldShowOgImage:L}=O();S(c(()=>({title:n.value,description:n.value,meta:[{property:"og:site_name",content:w},{property:"og:title",content:n.value},{property:"description",content:n.value},{property:"og:description",content:n.value},..._.value]})));const B=c(()=>{var m;const a=(m=e.value)==null?void 0:m.date;return a?j(a):null}),C=U,b=c(()=>d.name==="posts");return(a,m)=>{var h,k;const F=$,x=T("RouterView"),N=J,P=A,H=T("RouterLink");return s(),r(R,null,[t(L)?(s(),p(F,{key:0,date:(h=t(e))==null?void 0:h.date,title:(k=t(e))==null?void 0:k.title},null,8,["date","title"])):f("",!0),t(b)?(s(),p(x,{key:1})):(s(),p(P,{key:2},{header:g(()=>{var y,v;return[i(N,{list:(y=t(e))==null?void 0:y.tags},null,8,["list"]),o("h1",Q,u((v=t(e))==null?void 0:v.title),1),o("ul",W,[o("li",null,u(t(B)),1),X,o("li",null,"by "+u(t(C)),1)])]}),default:g(()=>[i(x)]),_:1})),t(b)?f("",!0):(s(),r("div",Y,[o("div",Z,[tt,o("div",et,[i(H,{class:"inline-flex w-full justify-between rounded-lg bg-black-400/10 p-6 font-semibold leading-none tracking-wider hover:bg-black-400/20 md:w-1/2",to:{name:"posts"}},{default:g(()=>[ot,i(t(M),{icon:t(q)},null,8,["icon"])]),_:1})])])]))],64)}}});typeof V=="function"&&V(st);export{st as default}; diff --git a/assets/bootstrap-in-nuxt-1bb6f56c.js b/assets/bootstrap-in-nuxt-1bb6f56c.js new file mode 100644 index 00000000..08c286c8 --- /dev/null +++ b/assets/bootstrap-in-nuxt-1bb6f56c.js @@ -0,0 +1,117 @@ +import{d as e,v as t,o as c,a as r,h as s,j as o,k as a,A as l}from"./app-033a95d9.js";const i={class:"markdown-body"},y=s("blockquote",null,[s("p",null,[a("此筆記指的是純 "),s("a",{href:"https://github.com/twbs/bootstrap"},"bootstrap"),a(",與 "),s("a",{href:"https://github.com/bootstrap-vue/bootstrap-vue"},"bootstrap-vue"),a(" 無關。")])],-1),A=s("blockquote",null,[s("p",null,[a("下文出現的 "),s("strong",null,"BS"),a(" 是 Bootstrap 的簡寫")])],-1),d={id:"%F0%9F%8C%9F-%E9%80%99%E7%AF%87%E7%AD%86%E8%A8%98%E7%9A%84%E9%87%8D%E9%BB%9E%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F",tabindex:"-1"},B=a("🌟 這篇筆記的重點有哪些? "),h={class:"header-anchor",href:"#%F0%9F%8C%9F-%E9%80%99%E7%AF%87%E7%AD%86%E8%A8%98%E7%9A%84%E9%87%8D%E9%BB%9E%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F"},u=s("p",null,"與直接匯入編譯過並打包好的 css 相比,此作法:",-1),D=s("ol",null,[s("li",null,"保留自定義樣式的能力,優雅地從根源自定義主題,免去用層層的自訂 class 複寫"),s("li",null,[a("避免組件充斥著 "),s("code",{class:""},"@media (min-width: 576px) { ... }"),a(" 諸如此類的魔法數字")]),s("li",null,"節省專案建構大小")],-1),E={id:"%F0%9F%94%A7-%E5%AE%89%E8%A3%9D-bootstrap",tabindex:"-1"},_=a("🔧 安裝 Bootstrap "),b={class:"header-anchor",href:"#%F0%9F%94%A7-%E5%AE%89%E8%A3%9D-bootstrap"},C=l(`
npm i bootstrap
+
npm i bootstrap
+
`,1),m={id:"%F0%9F%94%A7-%E5%AE%89%E8%A3%9D-%40nuxtjs%2Fstyle-resources",tabindex:"-1"},F=a("🔧 安裝 "),g=s("code",{class:""},"@nuxtjs/style-resources",-1),k=a(),f={class:"header-anchor",href:"#%F0%9F%94%A7-%E5%AE%89%E8%A3%9D-%40nuxtjs%2Fstyle-resources"},q=l(`
npm i @nuxtjs/style-resources
+
npm i @nuxtjs/style-resources
+
`,1),x={id:"%F0%9F%93%83-%E5%BB%BA%E7%AB%8B-_bootstrap.sass",tabindex:"-1"},v=a("📃 建立 "),S=s("code",{class:""},"_bootstrap.sass",-1),w=a(),j={class:"header-anchor",href:"#%F0%9F%93%83-%E5%BB%BA%E7%AB%8B-_bootstrap.sass"},$=l(`

建立 assets/sass/style-resources/_bootstrap.sass(檔名隨意)

在裡面匯入 BS 的源 SCSS 檔案,這三行是必須的。

// _bootstrap.sass
+
+@import "~bootstrap/scss/functions"
+@import "~bootstrap/scss/variables"
+@import "~bootstrap/scss/mixins"
+
// _bootstrap.sass
+
+@import "~bootstrap/scss/functions"
+@import "~bootstrap/scss/variables"
+@import "~bootstrap/scss/mixins"
+

https://getbootstrap.com/docs/4.6/getting-started/theming/#importing

`,4),N={id:"%F0%9F%93%83-%E5%BB%BA%E7%AB%8B-_custom.sass",tabindex:"-1"},V=a("📃 建立 "),R=s("code",{class:""},"_custom.sass",-1),T=a(),z={class:"header-anchor",href:"#%F0%9F%93%83-%E5%BB%BA%E7%AB%8B-_custom.sass"},G=l(`

建立 assets/sass/style-resources/_custom.sass(檔名隨意)。

此檔用來複寫 BS 預設變數。

// _custom.sass
+
+// 🌰 舉例:
+
+// 將「primary」換成黃色
+$theme-colors: (primary: #ffd019)
+
+// 將連結換成黃色,並移除 hover 的底線
+$link-color: #ffd019
+$link-hover-decoration: none
+
+// 重新定義斷點大小
+$grid-breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)
+
+/* ... */
+
// _custom.sass
+
+// 🌰 舉例:
+
+// 將「primary」換成黃色
+$theme-colors: (primary: #ffd019)
+
+// 將連結換成黃色,並移除 hover 的底線
+$link-color: #ffd019
+$link-hover-decoration: none
+
+// 重新定義斷點大小
+$grid-breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)
+
+/* ... */
+

更多可供複寫的變數都列在此處:
https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss

`,4),H={id:"%F0%9F%93%83-%E5%BB%BA%E7%AB%8B-style.sass",tabindex:"-1"},I=a("📃 建立 "),J=s("code",{class:""},"style.sass",-1),K=a(),L={class:"header-anchor",href:"#%F0%9F%93%83-%E5%BB%BA%E7%AB%8B-style.sass"},M=l(`

建立 assets/sass/style.sass(檔名隨意)

根據各自需求匯入 BS 元件,這些元件將會套用前面設定的主題變數。

// style.sass
+
+// 👍 推薦的元件
+@import "~bootstrap/scss/reboot"
+@import "~bootstrap/scss/utilities"
+@import "~bootstrap/scss/grid"
+@import "~bootstrap/scss/type"
+
+// 其他常用元件
+@import "~bootstrap/scss/images"
+@import "~bootstrap/scss/nav"
+@import "~bootstrap/scss/navbar"
+
+/* ... */
+
// style.sass
+
+// 👍 推薦的元件
+@import "~bootstrap/scss/reboot"
+@import "~bootstrap/scss/utilities"
+@import "~bootstrap/scss/grid"
+@import "~bootstrap/scss/type"
+
+// 其他常用元件
+@import "~bootstrap/scss/images"
+@import "~bootstrap/scss/nav"
+@import "~bootstrap/scss/navbar"
+
+/* ... */
+

可用的元件列表及用途請參考此處:
https://github.com/twbs/bootstrap/tree/main/scss

`,4),O={id:"%E2%9C%8F%EF%B8%8F-%E9%85%8D%E7%BD%AE-nuxt.config.js",tabindex:"-1"},P=a("✏️ 配置 "),Q=s("code",{class:""},"nuxt.config.js",-1),U=a(),W={class:"header-anchor",href:"#%E2%9C%8F%EF%B8%8F-%E9%85%8D%E7%BD%AE-nuxt.config.js"},X=l(`
export default {
+  /* ... */
+
+  styleResources: {
+    sass: [
+      './assets/sass/style-resources/_custom.sass',
+      './assets/sass/style-resources/_bootstrap.sass',
+    ],
+  },
+
+  css: [
+    './assets/sass/style.sass',
+  ],
+}
+
export default {
+  /* ... */
+
+  styleResources: {
+    sass: [
+      './assets/sass/style-resources/_custom.sass',
+      './assets/sass/style-resources/_bootstrap.sass',
+    ],
+  },
+
+  css: [
+    './assets/sass/style.sass',
+  ],
+}
+

⚠️ 注意
是在 css 匯入 style.sass,而不是在styleResources 匯入。
styleResources 匯入的話,將使得每個 Vue 組件都包含一大包的 Bootstrap 樣式,造成專案容量爆肥,編譯速度也會被嚴重拖慢。

`,2),Y={id:"%F0%9F%96%96-%E5%9C%A8-vue-%E7%B5%84%E4%BB%B6%E5%85%A7%E4%BD%BF%E7%94%A8",tabindex:"-1"},Z=a("🖖 在 Vue 組件內使用 "),ss={class:"header-anchor",href:"#%F0%9F%96%96-%E5%9C%A8-vue-%E7%B5%84%E4%BB%B6%E5%85%A7%E4%BD%BF%E7%94%A8"},as=l(`

🎉 大功告成!

如此便可自由自在地在 vue 組件內使用 BS 的 class 和所有 SCSS/SASS 變數。

<template>/* ... */</template>
+<script>/* ... */</script>
+
+<style lang="sass" scoped>
+div
+  @include media-breakpoint-down(sm)
+    background: $primary
+</style>
+
<template>/* ... */</template>
+<script>/* ... */</script>
+
+<style lang="sass" scoped>
+div
+  @include media-breakpoint-down(sm)
+    background: $primary
+</style>
+
`,3),es={title:"在 Nuxt 專案穩穩地匯入 Bootstrap",date:"2021/11/21",tags:["Nuxt","Vue","Bootstrap"],original:"https://hackmd.io/@xq/bootstrap-with-nuxt"},ts="",cs=e({__name:"bootstrap-in-nuxt",setup(ns,{expose:p}){return p({frontmatter:{title:"在 Nuxt 專案穩穩地匯入 Bootstrap",date:"2021/11/21",tags:["Nuxt","Vue","Bootstrap"],original:"https://hackmd.io/@xq/bootstrap-with-nuxt"},excerpt:void 0}),(os,ls)=>{const n=t("icon-link");return c(),r("div",i,[y,A,s("h2",d,[B,s("a",h,[o(n)])]),u,D,s("h2",E,[_,s("a",b,[o(n)])]),C,s("h2",m,[F,g,k,s("a",f,[o(n)])]),q,s("h2",x,[v,S,w,s("a",j,[o(n)])]),$,s("h2",N,[V,R,T,s("a",z,[o(n)])]),G,s("h2",H,[I,J,K,s("a",L,[o(n)])]),M,s("h2",O,[P,Q,U,s("a",W,[o(n)])]),X,s("h2",Y,[Z,s("a",ss,[o(n)])]),as])}}});export{cs as default,ts as excerpt,es as frontmatter}; diff --git a/assets/boss-7d1971fc.js b/assets/boss-7d1971fc.js new file mode 100644 index 00000000..5ee65d44 --- /dev/null +++ b/assets/boss-7d1971fc.js @@ -0,0 +1 @@ +import{d as r,v as c,o as i,a,h as s,j as o,k as e}from"./app-033a95d9.js";const _="/assets/boss1-d440988b.png",p="/assets/boss2-3442c269.png",h="/assets/boss3-5ed82877.png",m="/assets/boss4-2c92bcf2.png",d="/assets/boss5-c63dc0c0.png",l={class:"markdown-body"},b=s("p",null,"這是一個在線飲料購物系統 (Beverage Online Shop System),簡稱BOSS,是一個簡單易用,介面時尚清爽的飲料購物平台。本專案後端以PHP語言撰寫,前端以Bootstrap的架構搭配HTML5和CSS3作為基礎進行版型設計。",-1),g=s("p",null,[s("img",{src:_,alt:""})],-1),u=s("p",null,[e("在此系統中可以根據不同使用者身份,包括顧客、員工及管理員,提供各種不同的操作功能。"),s("br"),e(" 我們提供簡單直覺易懂的操作界面,讓顧客可以瀏覽本店所有商品、根據分類或搜尋條件取得商品資訊、對於產品及訂單進行打分或評論、查看。"),s("br"),e(" 員工可以上下架商品、管理商品之庫存及資訊、查看當前所有訂單資訊。管理者可以新增移除或修改會員資訊。")],-1),B={id:"%E4%BB%8B%E9%9D%A2",tabindex:"-1"},S=e("介面 "),f={class:"header-anchor",href:"#%E4%BB%8B%E9%9D%A2"},k=s("p",null,[s("img",{src:p,alt:"隨機展示熱銷商品"}),s("br"),s("img",{src:h,alt:"顯示當前優惠活動"}),s("br"),s("img",{src:m,alt:"商品列表"}),s("br"),s("img",{src:d,alt:"商品詳情頁面"})],-1),v={id:"demo",tabindex:"-1"},x=e("Demo "),y={class:"header-anchor",href:"#demo"},O=s("p",null,[s("s",null,[s("a",{href:"http://boss.ngseke.me/"},"http://boss.ngseke.me/")]),e(" 已下架")],-1),P=s("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=boss&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),U={title:"BOSS: Beverage Online Shop System",briefDescription:"線上飲料購物系統",githubLink:"https://github.com/ngseke/boss",period:["2017/11","2018/01"],members:["吳品頤","余鎧企","黃省喬","趙振廷"],cover:"/img/project-cover/boss.png",tags:["PHP","MySQL","UI/UX"]},j="",C=r({__name:"boss",setup(D,{expose:n}){return n({frontmatter:{title:"BOSS: Beverage Online Shop System",briefDescription:"線上飲料購物系統",githubLink:"https://github.com/ngseke/boss",period:["2017/11","2018/01"],members:["吳品頤","余鎧企","黃省喬","趙振廷"],cover:"/img/project-cover/boss.png",tags:["PHP","MySQL","UI/UX"]},excerpt:void 0}),(E,L)=>{const t=c("icon-link");return i(),a("div",l,[b,g,u,s("h2",B,[S,s("a",f,[o(t)])]),k,s("h2",v,[x,s("a",y,[o(t)])]),O,P])}}});export{C as default,j as excerpt,U as frontmatter}; diff --git a/assets/boss1-d440988b.png b/assets/boss1-d440988b.png new file mode 100644 index 00000000..1a51e4fd Binary files /dev/null and b/assets/boss1-d440988b.png differ diff --git a/assets/boss2-3442c269.png b/assets/boss2-3442c269.png new file mode 100644 index 00000000..53355304 Binary files /dev/null and b/assets/boss2-3442c269.png differ diff --git a/assets/boss3-5ed82877.png b/assets/boss3-5ed82877.png new file mode 100644 index 00000000..50f58134 Binary files /dev/null and b/assets/boss3-5ed82877.png differ diff --git a/assets/boss4-2c92bcf2.png b/assets/boss4-2c92bcf2.png new file mode 100644 index 00000000..03742796 Binary files /dev/null and b/assets/boss4-2c92bcf2.png differ diff --git a/assets/boss5-c63dc0c0.png b/assets/boss5-c63dc0c0.png new file mode 100644 index 00000000..b7f7fd2f Binary files /dev/null and b/assets/boss5-c63dc0c0.png differ diff --git a/assets/bumu1-2902b5f3.png b/assets/bumu1-2902b5f3.png new file mode 100644 index 00000000..03c46dec Binary files /dev/null and b/assets/bumu1-2902b5f3.png differ diff --git a/assets/bumu2-9fbc6c06.jpg b/assets/bumu2-9fbc6c06.jpg new file mode 100644 index 00000000..07b7c71b Binary files /dev/null and b/assets/bumu2-9fbc6c06.jpg differ diff --git a/assets/camp-2017-d632b91c.js b/assets/camp-2017-d632b91c.js new file mode 100644 index 00000000..4dfec138 --- /dev/null +++ b/assets/camp-2017-d632b91c.js @@ -0,0 +1 @@ +import{d as n,v as r,o as _,a as d,h as s,j as e,k as t,A as c}from"./app-033a95d9.js";const a="/assets/logo_fb-3a5fbf8a.png",p="/assets/icon-3f390287.png",m="/assets/bumu1-2902b5f3.png",l="/assets/logo_en-8478642b.png",h="/assets/bumu2-9fbc6c06.jpg",g="/assets/mingpai1-50edfb95.png",b="/assets/mingpai2-dead019a.png",E="/assets/tshirt1-4f8fb94c.png",f="/assets/tshirt2-c61c550c.png",B="/assets/hengfu-324e5744.png",A="/assets/fb_zhuanye-8a3106ac.png",u="/assets/tixing-f836dc82.png",x="/assets/dahezhao-4534134f.jpg",C="/assets/datoutie1-f730def3.png",D="/assets/datoutie2-b626c0ae.png",v="/assets/datoutie3-99933bc0.png",w="/assets/datoutie4-8d7f399d.png",k="/assets/datoutie_fb-b37840ad.png",T="/assets/0-11090461.png",F="/assets/1-f5f11eb3.png",N="/assets/2-a067597c.png",j="/assets/3-78ab6d35.png",I="/assets/4-708e3caa.png",y="/assets/5-0224294b.png",L="/assets/6-a159bc1d.png",V="/assets/7-cb414675.png",z="/assets/shouce1-193ed0a8.jpg",S="/assets/shouce2-0a3f097e.jpg",U={class:"markdown-body"},P=s("p",null,[s("img",{src:a,alt:""})],-1),q=s("p",null,[t("2017 年秋季"),s("em",null,"北科資工系"),t("和"),s("em",null,"北商會資系"),t("聯合舉辦的迎新活動,使用 Adobe Illustrator 設計與排版,圖標源自 "),s("a",{href:"https://www.flaticon.com"},"Flaticon"),t("。")],-1),G={id:"logo",tabindex:"-1"},H=t("Logo "),K={class:"header-anchor",href:"#logo"},Q=s("p",null,"Logo 分別展現出快炒鍋、辣椒、果汁和方糖的意象,與活動名稱相呼應。",-1),R=s("p",null,[s("img",{src:p,alt:"Logo圖標"}),s("br"),s("img",{src:m,alt:"Logo文字"}),s("br"),s("img",{src:l,alt:"英文版Logo文字"}),s("br"),s("img",{src:h,alt:"布幕"})],-1),Y={id:"%E5%90%8D%E7%89%8C",tabindex:"-1"},J=t("名牌 "),M={class:"header-anchor",href:"#%E5%90%8D%E7%89%8C"},O=s("p",null,[s("img",{src:g,alt:"名牌"}),s("br"),s("img",{src:b,alt:"名牌(英文)"})],-1),W={id:"t-shirt",tabindex:"-1"},X=t("T-SHIRT "),Z={class:"header-anchor",href:"#t-shirt"},$=s("p",null,[s("img",{src:E,alt:"營T"}),s("br"),s("img",{src:f,alt:"組T(背後)"})],-1),ss={id:"facebook-%E7%B2%89%E5%B0%88",tabindex:"-1"},ts=t("Facebook 粉專 "),os={class:"header-anchor",href:"#facebook-%E7%B2%89%E5%B0%88"},es=c('

大頭貼
橫幅
粉專實際使用情境
提醒事項
大合照標題

',1),cs={id:"facebook-%E5%A4%A7%E9%A0%AD%E8%B2%BC",tabindex:"-1"},as=t("Facebook 大頭貼 "),is={class:"header-anchor",href:"#facebook-%E5%A4%A7%E9%A0%AD%E8%B2%BC"},ns=c('

供工人宣傳期間替換的大頭貼,另有無頭版本可自由定制。
標準版
無頭版
定制範例 1
定制範例 2
大頭貼實際使用情境

',1),rs={id:"%E5%B0%8F%E6%89%8B%E5%86%8A",tabindex:"-1"},_s=t("小手冊 "),ds={class:"header-anchor",href:"#%E5%B0%8F%E6%89%8B%E5%86%8A"},ps=c('








小手冊印刷成品 1
小手冊實際印刷成品 2

',2),ms={id:"%E5%9B%9E%E9%A1%A7%E5%BD%B1%E7%89%87",tabindex:"-1"},ls=t("回顧影片 "),hs={class:"header-anchor",href:"#%E5%9B%9E%E9%A1%A7%E5%BD%B1%E7%89%87"},gs=s("p",null,"使用 Final Cut Pro 剪輯。",-1),bs={id:"%E5%B7%A5%E4%BA%BA%E5%9B%9E%E9%A1%A7%E5%BD%B1%E7%89%87",tabindex:"-1"},Es=t("工人回顧影片 "),fs={class:"header-anchor",href:"#%E5%B7%A5%E4%BA%BA%E5%9B%9E%E9%A1%A7%E5%BD%B1%E7%89%87"},Bs=s("div",{class:"embed-responsive"},[s("iframe",{class:"embed-responsive-item",src:"https://www.youtube.com/embed/xfxfVKGzyqY",allowfullscreen:""})],-1),As={id:"%E4%B8%89%E6%97%A5%E5%AD%B8%E5%93%A1%E5%9B%9E%E9%A1%A7%E5%BD%B1%E7%89%87",tabindex:"-1"},us=t("三日學員回顧影片 "),xs={class:"header-anchor",href:"#%E4%B8%89%E6%97%A5%E5%AD%B8%E5%93%A1%E5%9B%9E%E9%A1%A7%E5%BD%B1%E7%89%87"},Cs=s("div",{class:"embed-responsive"},[s("iframe",{class:"embed-responsive-item",src:"https://www.youtube.com/embed/57wQDmziAEw",allowfullscreen:""})],-1),Ts={title:"NTUT-CSIE & NTUB-ACC Camp, 2017",briefDescription:"迎新《會炒不加辣,果資不加糖》品牌識別設計",period:["2017/07","2017/08"],cover:"/img/project-cover/camp2017.png",tags:["Illustrator","Final Cut Pro"]},Fs="",Ns=n({__name:"camp-2017",setup(Ds,{expose:i}){return i({frontmatter:{title:"NTUT-CSIE & NTUB-ACC Camp, 2017",briefDescription:"迎新《會炒不加辣,果資不加糖》品牌識別設計",period:["2017/07","2017/08"],cover:"/img/project-cover/camp2017.png",tags:["Illustrator","Final Cut Pro"]},excerpt:void 0}),(vs,ws)=>{const o=r("icon-link");return _(),d("div",U,[P,q,s("h2",G,[H,s("a",K,[e(o)])]),Q,R,s("h2",Y,[J,s("a",M,[e(o)])]),O,s("h2",W,[X,s("a",Z,[e(o)])]),$,s("h2",ss,[ts,s("a",os,[e(o)])]),es,s("h2",cs,[as,s("a",is,[e(o)])]),ns,s("h2",rs,[_s,s("a",ds,[e(o)])]),ps,s("h2",ms,[ls,s("a",hs,[e(o)])]),gs,s("h3",bs,[Es,s("a",fs,[e(o)])]),Bs,s("h3",As,[us,s("a",xs,[e(o)])]),Cs])}}});export{Ns as default,Fs as excerpt,Ts as frontmatter}; diff --git a/assets/camp2017-8a21aa39.js b/assets/camp2017-8a21aa39.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/camp2017-8a21aa39.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/char2-9248d6c0.png b/assets/char2-9248d6c0.png new file mode 100644 index 00000000..d89a68d7 Binary files /dev/null and b/assets/char2-9248d6c0.png differ diff --git a/assets/char3-5ba16f0d.png b/assets/char3-5ba16f0d.png new file mode 100644 index 00000000..6c4a0d38 Binary files /dev/null and b/assets/char3-5ba16f0d.png differ diff --git a/assets/check-if-key-exists-ce0584a7.js b/assets/check-if-key-exists-ce0584a7.js new file mode 100644 index 00000000..fb7e6823 --- /dev/null +++ b/assets/check-if-key-exists-ce0584a7.js @@ -0,0 +1,133 @@ +import{d as e,v as c,o as t,a as r,h as s,j as l,k as n,A as p}from"./app-033a95d9.js";const y="/assets/valueOf-956b83dd.png",i={class:"markdown-body"},A={id:".hasownproperty()",tabindex:"-1"},B=s("code",{class:""},".hasOwnProperty()",-1),d=n(),D={class:"header-anchor",href:"#.hasownproperty()"},C=p(`

使用物件的 prototype hasOwnProperty 檢查。

不會遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+profile.hasOwnProperty('name') // true
+profile.hasOwnProperty('valueOf') // false
+
const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+profile.hasOwnProperty('name') // true
+profile.hasOwnProperty('valueOf') // false
+
`,3),h={id:"in",tabindex:"-1"},f=s("code",{class:""},"in",-1),E=n(),F={class:"header-anchor",href:"#in"},_=p(`

最簡潔的寫法,使用關鍵字 in 檢查。

但請留意 in 去尋找整個原型鏈,可能得到預期外的結果,並造成微乎其微的效能差異。

const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+'name' in profile // true
+
+/*
+  ⚠️ 雖然以下 key 並沒有在 \`profile\` 中明確定義,
+  但因為它們存在於物件的 prototype 中,所以依然會得到 true。
+*/
+'valueOf' in profile // true
+'toString' in profile // true
+'hasOwnProperty' in profile // true
+
const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+'name' in profile // true
+
+/*
+  ⚠️ 雖然以下 key 並沒有在 \`profile\` 中明確定義,
+  但因為它們存在於物件的 prototype 中,所以依然會得到 true。
+*/
+'valueOf' in profile // true
+'toString' in profile // true
+'hasOwnProperty' in profile // true
+

效能實測:hasOwnProperty 和 in 哪个性能高? - frosen的回答 - 知乎

',5),k={id:"object.prototype.hasownproperty.call()",tabindex:"-1"},u=s("code",{class:""},"Object.prototype.hasOwnProperty.call()",-1),g=n(),b={class:"header-anchor",href:"#object.prototype.hasownproperty.call()"},O=p(`

由於 JavaScript 未保護 hasOwnProperty,所以你完全可以複寫這個屬性,讓它刻意回傳錯誤的結果。

const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+/* 😢 現在無論傳入什麼都會得到 true */
+profile.hasOwnProperty('🍺') // true
+profile.hasOwnProperty(123) // true
+
const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+/* 😢 現在無論傳入什麼都會得到 true */
+profile.hasOwnProperty('🍺') // true
+profile.hasOwnProperty(123) // true
+

利用 Object.prototype.hasOwnProperty.call() 即可避免此情況發生,也不會遍歷原型鏈。

/* 😎 雖然冗長但最安全 */
+Object.prototype.hasOwnProperty.call(profile, 'name') // true
+Object.prototype.hasOwnProperty.call(profile, '🍺') // false
+
/* 😎 雖然冗長但最安全 */
+Object.prototype.hasOwnProperty.call(profile, 'name') // true
+Object.prototype.hasOwnProperty.call(profile, '🍺') // false
+
`,4),m={id:"object.hasown()",tabindex:"-1"},v=s("code",{class:""},"Object.hasOwn()",-1),w=n(),j={class:"header-anchor",href:"#object.hasown()"},S=p(`

ES13 推出的新特性,旨在取代 Object.prototype.hasOwnProperty(),寫相比之下法更直觀和簡潔。並且和 Object.prototype.hasOwnProperty.call() 一樣,即使複寫了 hasOwnProperty 依然可以得到正確的結果。

不會遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+Object.hasOwn(profile, 'name') // true
+Object.hasOwn(profile, '🍺') // false
+
const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+Object.hasOwn(profile, 'name') // true
+Object.hasOwn(profile, '🍺') // false
+
`,3),P={id:"%E5%85%B6%E4%BB%96%E5%B8%B8%E8%A6%8B%E4%BD%86%E6%9C%89%E9%99%B7%E9%98%B1%E7%9A%84%E6%96%B9%E6%B3%95",tabindex:"-1"},x=n("其他常見但有陷阱的方法 "),z={class:"header-anchor",href:"#%E5%85%B6%E4%BB%96%E5%B8%B8%E8%A6%8B%E4%BD%86%E6%9C%89%E9%99%B7%E9%98%B1%E7%9A%84%E6%96%B9%E6%B3%95"},J={id:"object.keys().includes()-%F0%9F%98%90",tabindex:"-1"},N=s("code",{class:""},"Object.keys().includes()",-1),q=n(" 😐 "),G={class:"header-anchor",href:"#object.keys().includes()-%F0%9F%98%90"},R=p(`

先以 Object.keys() 取得物件的所有 key 的陣列,接著呼叫陣列的方法 includes() 來檢查 key 是否存在。

但這個方法的時間複雜度是 O(n)或更高),因為它必須先至少遍歷物件來得到所有的 key,接著尋找時又得再遍歷一次陣列,在 key 數量一多時顯然很沒效率。

不會遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+const keys = Object.keys(profile) // ['name', 'age']
+
+keys.includes('name') // true
+keys.includes('valueOf') // false
+
const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+const keys = Object.keys(profile) // ['name', 'age']
+
+keys.includes('name') // true
+keys.includes('valueOf') // false
+
`,4),U={id:"!%3D%3D-undefined-%F0%9F%98%90",tabindex:"-1"},V=s("code",{class:""},"!== undefined",-1),W=n(" 😐 "),T={class:"header-anchor",href:"#!%3D%3D-undefined-%F0%9F%98%90"},H=p(`

當試圖存取不存在於物件的 key 時,會得到 undefined

但當某 key 存在而且值剛好是 undefined 時,那就仍會得到 false

遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  phone: undefined,
+}
+
+profile.address !== undefined // false
+profile.phone !== undefined // ⚠️ false
+
const profile = {
+  name: 'Sean',
+  phone: undefined,
+}
+
+profile.address !== undefined // false
+profile.phone !== undefined // ⚠️ false
+
`,4),I={id:"!!-%E6%88%96-boolean()-%F0%9F%98%95",tabindex:"-1"},K=s("code",{class:""},"!!",-1),L=n(" 或 "),M=s("code",{class:""},"Boolean()",-1),Q=n(" 😕 "),X={class:"header-anchor",href:"#!!-%E6%88%96-boolean()-%F0%9F%98%95"},Y=p(`

簡單暴力的寫法,也就是直接將值轉型成 boolean。但這方法顯然很不可靠,因為只要是 falsy 值,例如 0 、空字串 ''null 等 ,即使 key 存在但依然會得到 false

除非你對物件型別有十足的信心,例如在有 TypeScript 的場合,否則不太推薦這寫法。

遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  balance: 0,
+  isDead: false,
+}
+
+!!profile.name // true
+!!profile.balance // ⚠️ false
+Boolean(profile.isDead) // ⚠️ false
+
const profile = {
+  name: 'Sean',
+  balance: 0,
+  isDead: false,
+}
+
+!!profile.name // true
+!!profile.balance // ⚠️ false
+Boolean(profile.isDead) // ⚠️ false
+
`,4),Z={id:"%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99",tabindex:"-1"},$=n("參考資料 "),ss={class:"header-anchor",href:"#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99"},ns=s("ul",null,[s("li",null,[s("a",{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty"},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty")]),s("li",null,[s("a",{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn"},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn")])],-1),es={title:"檢查物件的 key 是否存在的 N 種方法",date:"2023/07/07",tags:["JavaScript"],original:"https://hackmd.io/@xq/check-if-key-exist"},cs="",ts=e({__name:"check-if-key-exists",setup(as,{expose:o}){return o({frontmatter:{title:"檢查物件的 key 是否存在的 N 種方法",date:"2023/07/07",tags:["JavaScript"],original:"https://hackmd.io/@xq/check-if-key-exist"},excerpt:void 0}),(ls,ps)=>{const a=c("icon-link");return t(),r("div",i,[s("h2",A,[B,d,s("a",D,[l(a)])]),C,s("h2",h,[f,E,s("a",F,[l(a)])]),_,s("h2",k,[u,g,s("a",b,[l(a)])]),O,s("h2",m,[v,w,s("a",j,[l(a)])]),S,s("h2",P,[x,s("a",z,[l(a)])]),s("h3",J,[N,q,s("a",G,[l(a)])]),R,s("h3",U,[V,W,s("a",T,[l(a)])]),H,s("h3",I,[K,L,M,Q,s("a",X,[l(a)])]),Y,s("h2",Z,[$,s("a",ss,[l(a)])]),ns])}}});export{ts as default,cs as excerpt,es as frontmatter}; diff --git a/assets/code-comments-be-like-59cec7d1.jpg b/assets/code-comments-be-like-59cec7d1.jpg new file mode 100644 index 00000000..7a41730b Binary files /dev/null and b/assets/code-comments-be-like-59cec7d1.jpg differ diff --git a/assets/code-review-taxi-number-a7cd2111.js b/assets/code-review-taxi-number-a7cd2111.js new file mode 100644 index 00000000..7eb26294 --- /dev/null +++ b/assets/code-review-taxi-number-a7cd2111.js @@ -0,0 +1,127 @@ +import{d as e,v as c,o as t,a as r,h as s,j as o,k as a,A as l}from"./app-033a95d9.js";const y="/assets/code-comments-be-like-59cec7d1.jpg",B={class:"markdown-body"},A={id:"%E5%89%8D%E8%A8%80",tabindex:"-1"},i=a("前言 "),C={class:"header-anchor",href:"#%E5%89%8D%E8%A8%80"},E=s("p",null,"Code Review 實在是太重要了,我想沒人會否認。",-1),d=s("p",null,"在軟體開發的世界中,需求不斷地變化是家常便飯,程式碼的可讀性和可維護性顯得格外重要。然而 Code Review 要看的多深、多仔細、花多少時間看,想找到適當的平衡點,其中分寸的拿捏確實是個挑戰。",-1),D=s("p",null,[a("但我始終認為有總比沒有好,畢竟 Code Review 是維持程式碼品質最有效的方式。相信大部分開發者都有同感: "),s("strong",null,"「不是新需求難實現,而是舊的扣改不動。」"),a(" 有多少人曾被困在遺留代碼之中,為了加一個新功能,首先得耗費無數的精力,試圖理解那些又長又冗、變數胡亂命名的程式碼。接著才發現函式和函式之間耦合的嚴重,想要重構又因為副作用太多而不敢輕舉妄動。")],-1),h=s("p",null,"因此在這篇文章中我想模擬這樣一個情境:",-1),F=s("blockquote",null,[s("p",null,"如果很嚴格地進行 Code Review,最吹毛求疵的那種,那看起來會像什麼樣子?")],-1),x=s("p",null,"我將以各種壞味道的角度,分析一段出現在真實場景的程式碼,並提供修改建議。同時也分享可套用的對應 ESLint 規則,其官方文件也會解釋這條規則存在的理由是什麼。",-1),u={id:"review-%E5%8E%9F%E5%89%87",tabindex:"-1"},_=a("Review 原則 "),g={class:"header-anchor",href:"#review-%E5%8E%9F%E5%89%87"},m=s("ol",null,[s("li",null,"程式碼應具可讀性"),s("li",null,"以盡量少的程式碼實現相同的功能"),s("li",null,"避免潛在副作用和壞味道")],-1),k={id:"%E9%9C%80%E6%B1%82%E6%8F%8F%E8%BF%B0",tabindex:"-1"},f=a("需求描述 "),v={class:"header-anchor",href:"#%E9%9C%80%E6%B1%82%E6%8F%8F%E8%BF%B0"},b=l('
  1. 函式接受任意字串,轉換成用來表示「計程車的數量」的數字字串,介於 030
  2. 小於 0 時應總是得到 0;大於 30 時總是應得到 30
  3. 無法用 parseInt 解析的字串一律轉換成 0
  4. 轉換後的字串不足兩位數時,左邊補上 0
',1),T={id:"%E5%8E%9F%E7%A8%8B%E5%BC%8F%E7%A2%BC%E7%89%87%E6%AE%B5",tabindex:"-1"},S=a("原程式碼片段 "),w={class:"header-anchor",href:"#%E5%8E%9F%E7%A8%8B%E5%BC%8F%E7%A2%BC%E7%89%87%E6%AE%B5"},R=l(`

這 13 行代碼包括註解一字不差地擷自真實專案,其中的人名已替換成假名

// 2021-11-23 Sean : 轉型 > 補0
+const TaxiNumber = (v) => {
+  // 正規表示
+  let regex = new RegExp(/^\\d{1,3}$/);
+  // 叫車最小值、最大值
+  let minTaxi = 0, maxTaxi = 30;
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  v = parseInt(v);
+  // 字串判斷?數值判斷
+  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+  // 回傳 & 補0
+  return v.toString().padStart(2, '0');
+}
+
// 2021-11-23 Sean : 轉型 > 補0
+const TaxiNumber = (v) => {
+  // 正規表示
+  let regex = new RegExp(/^\\d{1,3}$/);
+  // 叫車最小值、最大值
+  let minTaxi = 0, maxTaxi = 30;
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  v = parseInt(v);
+  // 字串判斷?數值判斷
+  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+  // 回傳 & 補0
+  return v.toString().padStart(2, '0');
+}
+
`,2),j={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E4%B8%80%EF%BC%9A%E6%84%8F%E5%9C%96%E4%B8%8D%E6%98%8E%E7%9A%84%E5%87%BD%E5%BC%8F%E5%91%BD%E5%90%8D",tabindex:"-1"},q=a("壞味道一:意圖不明的函式命名 "),N={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E4%B8%80%EF%BC%9A%E6%84%8F%E5%9C%96%E4%B8%8D%E6%98%8E%E7%9A%84%E5%87%BD%E5%BC%8F%E5%91%BD%E5%90%8D"},I=l(`
const TaxiNumber = (v) => {
+      ^^^^^^^^^^
+
const TaxiNumber = (v) => {
+      ^^^^^^^^^^
+

我首先注意到 TaxiNumber 這個單詞是大駝峰,當讀者單獨看時很容易產生困惑:它是用來展示計程車數字的 React Component 嗎?又或是 class?但都不是,它只是用來轉換字串的函式。

函式通常是以動詞開頭,而且應命名得清晰明瞭,能夠表達其功能或操作目的。

這類函式建議以 convertparseformatnormalize 等動詞開頭,例如 convertAToB,且按照 JavaScript 慣例通常會以小駝峰命名。

💡 套用 ESLint 規則 camelcase 可約束一致變數的命名風格。

`,5),M={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E4%BA%8C%EF%BC%9A%E7%B8%AE%E5%AF%AB%E7%9A%84%E5%8F%83%E6%95%B8",tabindex:"-1"},L=a("壞味道二:縮寫的參數 "),$={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E4%BA%8C%EF%BC%9A%E7%B8%AE%E5%AF%AB%E7%9A%84%E5%8F%83%E6%95%B8"},J=l(`
const TaxiNumber = (v) => {
+                    ^
+
const TaxiNumber = (v) => {
+                    ^
+

v 可能是 value 的縮寫,隨個人喜好略縮單詞是個常見的壞味道,通常只有作者本人才知道其背後的含義。但要知道身為團隊的一員,自己不會是唯一維護這段程式碼的人。命名前多花個幾秒仔細思考,將心比心地為下一個接手的人,甚至是一個月後的自己著想,確保它清晰易懂,任何人看了都能夠馬上理解。

建議替換成 countString ,或是任意一具描述性單詞。

💡 套用 ESLint 規則 id-denylist 可以定義「禁詞」,禁止使用這些單詞來命名,例如單字母 a b c,或是毫無意義的 datavalue

`,4),V={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E4%B8%89%EF%BC%9A%E5%86%97%E4%BD%99%E7%9A%84%E5%BB%BA%E6%A7%8B%E5%BC%8F",tabindex:"-1"},K=a("壞味道三:冗余的建構式 "),z={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E4%B8%89%EF%BC%9A%E5%86%97%E4%BD%99%E7%9A%84%E5%BB%BA%E6%A7%8B%E5%BC%8F"},O=l(`
  let regex = new RegExp(/^\\d{1,3}$/);
+              ^^^^^^^^^^
+
  let regex = new RegExp(/^\\d{1,3}$/);
+              ^^^^^^^^^^
+

在這個情境並沒打算以 template literals 來動態產生 pattern,例如 new RegExp(\`^{\${a}}$\`),所以可直接省略掉 new RegExp(),直接寫成更簡短的 /^\\d{1,3}$/ 即可。

💡 套用 ESLint 規則 prefer-regex-literals 來偏好採用 regular expression literals 寫法。

當能用更簡潔的寫法達成一模一樣的效果時,就沒理由選擇冗長的那個。其他類似的冗余的建構式還有這些:

  • {} ,而非 new Object()
  • [] ,而非 new Array()

💡 套用 ESLint 規則 no-new-objectno-array-constructor 來偏好採用 literal notation 寫法。

`,6),U={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E5%9B%9B%EF%BC%9A%E5%BE%9E%E6%9C%AA%E6%94%B9%E8%AE%8A%E7%9A%84-let",tabindex:"-1"},W=a("壞味道四:從未改變的 "),G=s("code",{class:""},"let",-1),H=a(),P={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E5%9B%9B%EF%BC%9A%E5%BE%9E%E6%9C%AA%E6%94%B9%E8%AE%8A%E7%9A%84-let"},Q=l(`
  let minTaxi = 0, maxTaxi = 30;
+  ^^^
+
  let minTaxi = 0, maxTaxi = 30;
+  ^^^
+

一個簡單的原則是永遠優先使用 const 來宣告變數,除非真的有需要重新賦值。因此建議改寫成:

/* 常見風格 */
+const min = 0, max = 30
+
+/* 陣列解構風格 */
+const [min, max] = [0, 30]
+
/* 常見風格 */
+const min = 0, max = 30
+
+/* 陣列解構風格 */
+const [min, max] = [0, 30]
+

順帶一提, [number, number] 的寫法在 TypeScript 中稱作 tuple,在這隱含一種成雙成對的概念,有助於讀者把它們理解成是「一組範圍」的數字。

💡 套用 ESLint 規則 prefer-const 檢查從未被重新賦值的變數。支援 auto fix,可以自動將 let 替換成 const

`,5),X={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E4%BA%94%EF%BC%9A%E5%B0%8D%E5%8F%83%E6%95%B8%E9%87%8D%E6%96%B0%E8%B3%A6%E5%80%BC",tabindex:"-1"},Y=a("壞味道五:對參數重新賦值 "),Z={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E4%BA%94%EF%BC%9A%E5%B0%8D%E5%8F%83%E6%95%B8%E9%87%8D%E6%96%B0%E8%B3%A6%E5%80%BC"},ss=l(`
  v = parseInt(v);
+    ^
+
  v = parseInt(v);
+    ^
+

無論任何語言,複寫函式的參數從來不是個好主意,可參考一些針對 reassign parameter 的討論。因為這很容易誤導讀者,原本預期傳入的參數是 string,但突然在某一行後卻硬生生變成了 number。

若專案有導入 TypeScript,某種程度也可以避免出現這種寫法,例如在此例中,因為新值顯然不符合參數原本的型別,compiler 便會直接報錯。

💡 套用 ESLint 規則 no-param-reassign 來防止參數被 reassign。

`,4),as={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E5%85%AD%EF%BC%9A%E5%86%97%E9%95%B7%E7%9A%84%E4%B8%89%E5%85%83%E9%81%8B%E7%AE%97%E5%AD%90",tabindex:"-1"},ns=a("壞味道六:冗長的三元運算子 "),os={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E5%85%AD%EF%BC%9A%E5%86%97%E9%95%B7%E7%9A%84%E4%B8%89%E5%85%83%E9%81%8B%E7%AE%97%E5%AD%90"},ls=l(`
  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+

巢狀的三元運算子,多個 :? 交錯,沒有換行與縮排,缺乏可讀性。

建議採用具有更明確的語意的 Math.min()Math.max() 方法改寫,並加上適當的換行和縮排。

  v = regex.test(v.toString())
+    ? Math.min(Math.max(v, minTaxi), maxTaxi)
+    : minTaxi
+
  v = regex.test(v.toString())
+    ? Math.min(Math.max(v, minTaxi), maxTaxi)
+    : minTaxi
+

💡 套用 ESLint 規則 no-nested-ternary 來禁止巢狀的三元運算子。

`,5),ps={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E4%B8%83%EF%BC%9A%E6%B7%B1%E5%85%A5%E8%AE%8A%E6%95%B8%E7%9A%84%E5%91%BD%E5%90%8D%E8%97%9D%E8%A1%93",tabindex:"-1"},es=a("壞味道七:深入變數的命名藝術 "),cs={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E4%B8%83%EF%BC%9A%E6%B7%B1%E5%85%A5%E8%AE%8A%E6%95%B8%E7%9A%84%E5%91%BD%E5%90%8D%E8%97%9D%E8%A1%93"},ts={id:"taxinumber",tabindex:"-1"},rs=s("code",{class:""},"TaxiNumber",-1),ys=a(),Bs={class:"header-anchor",href:"#taxinumber"},As=s("p",null,[s("code",{class:""},"number"),a(" 一詞作為變數有點太籠統了,也沒有好好地起到描述的作用,說到底任何只要是數值想必都會是 number。例如寬度、角度和價格它們都是 number。")],-1),is=s("p",null,[a("要描述這類變數,相信一定找得到比 "),s("code",{class:""},"number"),a(" 還更貼切的單詞。")],-1),Cs=s("p",null,[a("根據需求它是用來表示「計程車的數量」,對於這類"),s("strong",null,"計數/累加"),a("的變數,建議可命名成 "),s("code",{class:""},"count"),a(" 。")],-1),Es={id:"mintaxi",tabindex:"-1"},ds=s("code",{class:""},"minTaxi",-1),Ds=a(),hs={class:"header-anchor",href:"#mintaxi"},Fs=l('

第一眼見到 minTaximaxTaxi ,有點難想像其背後的含義是什麼。讀者可能會把他誤解成車型的大小,或是計程車的載客數。

在這個情況建議單純命名成 min 就行,把 taxi 當作後綴反而不適合,因為實際上要描述的是計程車的「數量」而非「計程車本身」。若想更精準的描述,可以考慮用 minCountminTaxiCount

',2),xs={id:"%E5%A3%9E%E5%91%B3%E9%81%93%E5%85%AB%EF%BC%9A%E3%80%8C%2F%2F-%E5%88%B0%E6%AD%A4%E4%B8%80%E9%81%8A%E3%80%8D",tabindex:"-1"},us=a("壞味道八:「"),_s=s("code",{class:""},"// 到此一遊",-1),gs=a("」 "),ms={class:"header-anchor",href:"#%E5%A3%9E%E5%91%B3%E9%81%93%E5%85%AB%EF%BC%9A%E3%80%8C%2F%2F-%E5%88%B0%E6%AD%A4%E4%B8%80%E9%81%8A%E3%80%8D"},ks=l(`
// 2021-11-23 Sean : 轉型 > 補0
+
// 2021-11-23 Sean : 轉型 > 補0
+

在未導入版控的遺留專案中,常見在註解裡出現作者的大名。但在現代軟體開發早以 git 進行版控,透過 git blame 就能追溯每一行的修改者是誰、何時修改的、以及對應的 commit message 等。

  // 正規表示
+  // 叫車最小值、最大值
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  // 字串判斷?數值判斷
+  // 回傳 & 補0
+
  // 正規表示
+  // 叫車最小值、最大值
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  // 字串判斷?數值判斷
+  // 回傳 & 補0
+

事實上,不只第一行的註解,這整段所有的註解可能都是多餘的,因為程式碼做的事太顯而易見了,註解反而變成閱讀時的雜訊。善用能夠自我描述的變數和函式命名是最好的方法,因為它們本身就能夠清楚地傳達代碼的意圖,不再需要額外的註解。

code comment be like

',5),fs={id:"%E6%9C%80%E7%B5%82%E9%87%8D%E6%A7%8B%E7%B5%90%E6%9E%9C",tabindex:"-1"},vs=a("最終重構結果 "),bs={class:"header-anchor",href:"#%E6%9C%80%E7%B5%82%E9%87%8D%E6%A7%8B%E7%B5%90%E6%9E%9C"},Ts=l(`

經過分析不難發現這個函式沒必要用上 RegExp,單純用 parseInt() 就足夠了。因為當傳入不合法的字串時,它就會一律回傳 NaN,也不會拋出任何錯誤。KISS(Keep It Simple, Stupid)原則便很好地說明了這個道理,複雜的設計往往容易出錯,增加不必要的複雜度和 debug 的難度,因此應傾向以簡單和直接的方式解決問題,以保持程式碼的清晰度和可讀性。

綜合上述幾點,經過 TypeScript 重構後的程式碼可以長得像這樣:

const parseTaxiCount = (countString: string) => {
+  const [min, max] = [0, 30]
+  const count = parseInt(countString) || 0
+  const clampedCount = Math.min(Math.max(count, min), max)
+  const paddedCount = String(clampedCount).padStart(2, '0')
+
+  return paddedCount
+}
+
const parseTaxiCount = (countString: string) => {
+  const [min, max] = [0, 30]
+  const count = parseInt(countString) || 0
+  const clampedCount = Math.min(Math.max(count, min), max)
+  const paddedCount = String(clampedCount).padStart(2, '0')
+
+  return paddedCount
+}
+

最後也附上單元測試(Jest):

it('parseTaxiCount()', () => {
+  expect(parseTaxiCount('')).toBe('00')
+  expect(parseTaxiCount(' ')).toBe('00')
+  expect(parseTaxiCount('-1')).toBe('00')
+  expect(parseTaxiCount('0')).toBe('00')
+  expect(parseTaxiCount('1')).toBe('01')
+  expect(parseTaxiCount('01')).toBe('01')
+  expect(parseTaxiCount('15')).toBe('15')
+  expect(parseTaxiCount('30')).toBe('30')
+  expect(parseTaxiCount('31')).toBe('30')
+  expect(parseTaxiCount('999')).toBe('30')
+  expect(parseTaxiCount('abc')).toBe('00')
+  expect(parseTaxiCount('1a1')).toBe('01')
+  expect(parseTaxiCount('30aaa')).toBe('30')
+  expect(parseTaxiCount('030aaa')).toBe('30')
+})
+
it('parseTaxiCount()', () => {
+  expect(parseTaxiCount('')).toBe('00')
+  expect(parseTaxiCount(' ')).toBe('00')
+  expect(parseTaxiCount('-1')).toBe('00')
+  expect(parseTaxiCount('0')).toBe('00')
+  expect(parseTaxiCount('1')).toBe('01')
+  expect(parseTaxiCount('01')).toBe('01')
+  expect(parseTaxiCount('15')).toBe('15')
+  expect(parseTaxiCount('30')).toBe('30')
+  expect(parseTaxiCount('31')).toBe('30')
+  expect(parseTaxiCount('999')).toBe('30')
+  expect(parseTaxiCount('abc')).toBe('00')
+  expect(parseTaxiCount('1a1')).toBe('01')
+  expect(parseTaxiCount('30aaa')).toBe('30')
+  expect(parseTaxiCount('030aaa')).toBe('30')
+})
+
`,5),Ss={id:"%E7%B5%90%E8%AA%9E",tabindex:"-1"},ws=a("結語 "),Rs={class:"header-anchor",href:"#%E7%B5%90%E8%AA%9E"},js=s("p",null,"以上分析基於個人開發習慣,提供一些在 Code Review 時可作為參考的觀點,其中很多屬於很主觀的意見,未必是最佳解。因此每個團隊都應該對程式碼風格有所共識,你會發現其實絕大部分都可以交給 ESLint 等自動化工具檢查,將寶貴的 Code Review 時間花在確認是否符合需求、架構的設計是否合理、評估演算法的效能和擴展性等。",-1),qs=s("p",null,"Code Review 的目的不僅僅是確保程式碼的品質和一致性,更重要的是促進團隊之間的合作和溝通。這是一個讓開發者互相學習、分享最佳實踐和技巧,以及達成共識的過程。因此在這個過程中,要盡可能保持開放和建設性的討論,避免淪為單純的批評和指責。",-1),$s={title:"如果太認真去 Code Review 會發生什麼事?",date:"2023/05/31",tags:["JavaScript"]},Js="",Vs=e({__name:"code-review-taxi-number",setup(Ns,{expose:p}){return p({frontmatter:{title:"如果太認真去 Code Review 會發生什麼事?",date:"2023/05/31",tags:["JavaScript"]},excerpt:void 0}),(Is,Ms)=>{const n=c("icon-link");return t(),r("div",B,[s("h2",A,[i,s("a",C,[o(n)])]),E,d,D,h,F,x,s("h3",u,[_,s("a",g,[o(n)])]),m,s("h2",k,[f,s("a",v,[o(n)])]),b,s("h2",T,[S,s("a",w,[o(n)])]),R,s("h2",j,[q,s("a",N,[o(n)])]),I,s("h2",M,[L,s("a",$,[o(n)])]),J,s("h2",V,[K,s("a",z,[o(n)])]),O,s("h2",U,[W,G,H,s("a",P,[o(n)])]),Q,s("h2",X,[Y,s("a",Z,[o(n)])]),ss,s("h2",as,[ns,s("a",os,[o(n)])]),ls,s("h2",ps,[es,s("a",cs,[o(n)])]),s("h3",ts,[rs,ys,s("a",Bs,[o(n)])]),As,is,Cs,s("h3",Es,[ds,Ds,s("a",hs,[o(n)])]),Fs,s("h2",xs,[us,_s,gs,s("a",ms,[o(n)])]),ks,s("h2",fs,[vs,s("a",bs,[o(n)])]),Ts,s("h2",Ss,[ws,s("a",Rs,[o(n)])]),js,qs])}}});export{Vs as default,Js as excerpt,$s as frontmatter}; diff --git a/assets/command-reload-window-955490ff.png b/assets/command-reload-window-955490ff.png new file mode 100644 index 00000000..2917f6e0 Binary files /dev/null and b/assets/command-reload-window-955490ff.png differ diff --git a/assets/competition-1f600c5f.png b/assets/competition-1f600c5f.png new file mode 100644 index 00000000..b802b52a Binary files /dev/null and b/assets/competition-1f600c5f.png differ diff --git a/assets/component-naming-87122709.js b/assets/component-naming-87122709.js new file mode 100644 index 00000000..87d165e6 --- /dev/null +++ b/assets/component-naming-87122709.js @@ -0,0 +1,43 @@ +import{d as e,v as t,o as c,a as r,h as s,j as l,k as n,A as o}from"./app-033a95d9.js";const i="/assets/go-to-file-32915399.png",B="/assets/tabs-fc5ce7c7.png",y="/assets/open-editors-844d14f7.png",A={class:"markdown-body"},E={id:"%E5%89%8D%E8%A8%80%EF%BC%9A30-%E7%A7%92%E8%B6%8A%E5%8D%97%E8%AA%9E%E5%B0%8F%E6%95%99%E5%AE%A4-%F0%9F%87%BB%F0%9F%87%B3",tabindex:"-1"},d=n("前言:30 秒越南語小教室 🇻🇳 "),h={class:"header-anchor",href:"#%E5%89%8D%E8%A8%80%EF%BC%9A30-%E7%A7%92%E8%B6%8A%E5%8D%97%E8%AA%9E%E5%B0%8F%E6%95%99%E5%AE%A4-%F0%9F%87%BB%F0%9F%87%B3"},g=o(`

越南語是形容詞後置的語言,恰恰和華語或英語相反。

例如「越南語」這個詞會寫成 「語越南」

Tiếng Việt Nam
+ ()  ( 越南 )
+
Tiếng Việt Nam
+ ()  ( 越南 )
+

「台灣人」則會寫做 「人台灣」

Người Đài Loan
+ ()  ( 台灣 )
+
Người Đài Loan
+ ()  ( 台灣 )
+
`,5),C={id:"%E4%BF%AE%E9%A3%BE%E8%AA%9E%E6%93%BA%E5%BE%8C%E9%9D%A2",tabindex:"-1"},D=n("修飾語擺後面 "),_={class:"header-anchor",href:"#%E4%BF%AE%E9%A3%BE%E8%AA%9E%E6%93%BA%E5%BE%8C%E9%9D%A2"},x=o('

在 Vue 官方風格指南中,組件命名的概念與越南語文法不謀而合。

若按照英語的文法,人們通常習慣這樣命名組件:

  • Primary (a.) + Button (n.) = PrimaryButton
  • Date (n.) + TextField (n.) = DateTextField
  • Confirm (v.) + Dialog (n.) = ConfirmDialog

但如指南所述,它認為應該把🔵主語/主詞(文中所謂高級別的詞)擺到前面;🔴修飾性的詞則是放在結尾。形容詞、動詞或名詞都可算是修飾詞。

因此推薦的命名法如下:

  • Button (n.) + Primary (a.) = ButtonPrimary
  • TextField (n.) + Date (n.) = TextFieldDate
  • Dialog (n.) + Confirm (v.) = DialogConfirm
',6),m={id:"%E5%9C%A8%E7%89%B9%E5%AE%9A%E8%AA%9E%E5%A2%83%E5%85%A7%E7%9A%84%E7%B5%84%E4%BB%B6",tabindex:"-1"},u=n("在特定語境內的組件 "),k={class:"header-anchor",href:"#%E5%9C%A8%E7%89%B9%E5%AE%9A%E8%AA%9E%E5%A2%83%E5%85%A7%E7%9A%84%E7%B5%84%E4%BB%B6"},F=o('

對於在🟢特定語境內才有意義的的組件──也就是在某些個別頁面,或是只有在特定組件中用得到的子組件,指南中也建議以父組件或頁面名當作前綴

例如:

  • Search (父組件) + TextField + Date = SearchTextFieldDate
    Search 組件內專用的日期輸入框
  • Search (父組件) + Button + Submit = SearchButtonSubmit
    Search 組件內專用的送出按鈕
  • LandingPage (頁面) + List + News = LandingPageListNews
    只在 Landing Page 用到的新聞列表
  • LandingPage (頁面) + List + Features = LandingPageListFeatures
    只在 Landing Page 用到的產品特色列表
',3),f={id:"%E5%81%9C%E6%AD%A2%E5%B7%A2%E7%8B%80%E8%B3%87%E6%96%99%E5%A4%BE%EF%BC%81",tabindex:"-1"},L=n("停止巢狀資料夾! "),b={class:"header-anchor",href:"#%E5%81%9C%E6%AD%A2%E5%B7%A2%E7%8B%80%E8%B3%87%E6%96%99%E5%A4%BE%EF%BC%81"},v=o(`

你可能試過以一層又一層的目錄來區分組件,像是:

components/
+  |- LandingPage/
+     |- News/
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+     |- Features.tsx
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+
components/
+  |- LandingPage/
+     |- News/
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+     |- Features.tsx
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+

但你其實可以放心、大膽地將它們都放在同一層資料夾。

重新調整後的目錄結構如下:

components/
+  |- LandingPageNewsItem.tsx
+  |- LandingPageNewsList.tsx
+  |- LandingPageNewsTitle.tsx
+  |- LandingPageFeaturesItem.tsx
+  |- LandingPageFeaturesList.tsx
+  |- LandingPageFeaturesTitle.tsx
+
components/
+  |- LandingPageNewsItem.tsx
+  |- LandingPageNewsList.tsx
+  |- LandingPageNewsTitle.tsx
+  |- LandingPageFeaturesItem.tsx
+  |- LandingPageFeaturesList.tsx
+  |- LandingPageFeaturesTitle.tsx
+
`,5),P={id:"but-why%3F",tabindex:"-1"},N=n("But Why? "),T={class:"header-anchor",href:"#but-why%3F"},w={id:"%E6%AA%94%E6%A1%88%E7%B8%BD%E7%AE%A1%E7%9A%84%E6%8E%92%E5%BA%8F",tabindex:"-1"},S=n("檔案總管的排序 "),I={class:"header-anchor",href:"#%E6%AA%94%E6%A1%88%E7%B8%BD%E7%AE%A1%E7%9A%84%E6%8E%92%E5%BA%8F"},V=s("p",null,[n("絕大多數 IDE 例如 VSCode,它們的檔案總管都是按照"),s("strong",null,"英文字母"),n("排序的,所以前綴相同的那些文件,都會集中在一塊兒,從命名一眼就能看出哪些組件具有較高關聯性。")],-1),j={id:"%E3%80%8C%E6%BB%BE%E5%8B%95%E3%80%8D%E6%AF%94%E3%80%8C%E5%B1%95%E9%96%8B%E8%B3%87%E6%96%99%E5%A4%BE%E3%80%8D%E6%9B%B4%E7%84%A1%E8%B2%A0%E6%93%94",tabindex:"-1"},z=n("「滾動」比「展開資料夾」更無負擔 "),q={class:"header-anchor",href:"#%E3%80%8C%E6%BB%BE%E5%8B%95%E3%80%8D%E6%AF%94%E3%80%8C%E5%B1%95%E9%96%8B%E8%B3%87%E6%96%99%E5%A4%BE%E3%80%8D%E6%9B%B4%E7%84%A1%E8%B2%A0%E6%93%94"},R=s("p",null,"若要從茫茫組件庫的大海中找尋某個組件,一個一個點擊並展開深層的巢狀資料夾是非常惱人的。更別提有些打開、有些關閉的資料夾相互交錯混雜的畫面,看了更是令人煩躁不安。",-1),G=s("p",null,"相比之下,用滑鼠滾動在同一層目錄瀏覽,顯然更有效率且輕鬆許多。",-1),W={id:"%E7%A7%BB%E5%8B%95%E7%B5%84%E4%BB%B6%E7%9A%84%E5%9B%B0%E9%9B%A3%E5%BA%A6",tabindex:"-1"},Y=n("移動組件的困難度 "),H={class:"header-anchor",href:"#%E7%A7%BB%E5%8B%95%E7%B5%84%E4%BB%B6%E7%9A%84%E5%9B%B0%E9%9B%A3%E5%BA%A6"},J=s("p",null,"對於尚未導入 TypeScript 的專案,想移動組件可能是件大工程。因為 VSCode 可能無法理解組件之間的相依關係,而無法順利地自動進行重構。",-1),K=s("p",null,[n("假設有一個組件 "),s("code",{class:""},"components/searchBar/Input/DateIuput.tsx"),n(",今天我想將它提升至上層的 "),s("code",{class:""},"components/Input/"),n(" 目錄,讓其他更多組件共用。然而我別無選擇,只能在專案中進行的全域搜尋並取代路徑字串,同時也得背負意外替換到其他無辜字串的風險。")],-1),M={id:"%E6%92%9E%E5%90%8D%E7%B5%84%E4%BB%B6%E5%9C%A8%E8%A6%96%E8%A6%BA%E4%B8%8A%E9%9B%A3%E4%BB%A5%E5%AE%9A%E4%BD%8D",tabindex:"-1"},O=n("撞名組件在視覺上難以定位 "),Q={class:"header-anchor",href:"#%E6%92%9E%E5%90%8D%E7%B5%84%E4%BB%B6%E5%9C%A8%E8%A6%96%E8%A6%BA%E4%B8%8A%E9%9B%A3%E4%BB%A5%E5%AE%9A%E4%BD%8D"},U=s("p",null,[n("利用 "),s("code",{class:""},"⌘ + P"),n(" 搜尋眾多同名的組件時,在 VSCode 裡很難快速定位到目標,因為它們乍看之下都一模一樣。")],-1),X=s("p",null,[s("img",{src:i,alt:""})],-1),Z=s("p",null,"同樣地,出現在頁籤中的多個同名檔案也是讓人眼花撩亂。",-1),$=s("p",null,[s("img",{src:B,alt:""})],-1),ss=s("p",null,[s("img",{src:y,alt:""})],-1),ns={id:"%E7%B5%90%E8%AA%9E",tabindex:"-1"},as=n("結語 "),ls={class:"header-anchor",href:"#%E7%B5%90%E8%AA%9E"},os=s("p",null,[n("本文中所提到的命名方式並非強制性的,它只是一種風格,在實務上還是應該考量專案的規模,根據團隊固有的習慣來權衡。不過如果今天你要做的是個人的 side project(例如"),s("a",{href:"https://github.com/ngseke/ngseke.me"},"本專案"),n("),那我會非常推薦你不如大膽地嘗試看看。")],-1),ps={id:"%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99",tabindex:"-1"},es=n("參考資料 "),ts={class:"header-anchor",href:"#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99"},cs=s("ul",null,[s("li",null,[s("a",{href:"https://zh.wikipedia.org/zh-tw/%E8%B6%8A%E5%8D%97%E8%AA%9E%E6%96%87%E6%B3%95"},"https://zh.wikipedia.org/zh-tw/越南語文法")]),s("li",null,[s("a",{href:"https://v2.cn.vuejs.org/v2/style-guide"},"https://v2.cn.vuejs.org/v2/style-guide")])],-1),As={title:"試著用有點違反直覺的方式命名組件",date:"2021/11/28",tags:["Vue","React"],original:"https://hackmd.io/@xq/component-naming-reversely"},Es="",ds=e({__name:"component-naming",setup(rs,{expose:p}){return p({frontmatter:{title:"試著用有點違反直覺的方式命名組件",date:"2021/11/28",tags:["Vue","React"],original:"https://hackmd.io/@xq/component-naming-reversely"},excerpt:void 0}),(is,Bs)=>{const a=t("icon-link");return c(),r("div",A,[s("h2",E,[d,s("a",h,[l(a)])]),g,s("h2",C,[D,s("a",_,[l(a)])]),x,s("h2",m,[u,s("a",k,[l(a)])]),F,s("h2",f,[L,s("a",b,[l(a)])]),v,s("h2",P,[N,s("a",T,[l(a)])]),s("h3",w,[S,s("a",I,[l(a)])]),V,s("h3",j,[z,s("a",q,[l(a)])]),R,G,s("h3",W,[Y,s("a",H,[l(a)])]),J,K,s("h3",M,[O,s("a",Q,[l(a)])]),U,X,Z,$,ss,s("h2",ns,[as,s("a",ls,[l(a)])]),os,s("h2",ps,[es,s("a",ts,[l(a)])]),cs])}}});export{ds as default,Es as excerpt,As as frontmatter}; diff --git a/assets/config-21adacbc.png b/assets/config-21adacbc.png new file mode 100644 index 00000000..51201fca Binary files /dev/null and b/assets/config-21adacbc.png differ diff --git a/assets/cover-37fabbf0.png b/assets/cover-37fabbf0.png new file mode 100644 index 00000000..2482442e Binary files /dev/null and b/assets/cover-37fabbf0.png differ diff --git a/assets/cover-76f3ee6d.png b/assets/cover-76f3ee6d.png new file mode 100644 index 00000000..8fade8b1 Binary files /dev/null and b/assets/cover-76f3ee6d.png differ diff --git a/assets/cover-background-f6446981.jpg b/assets/cover-background-f6446981.jpg new file mode 100644 index 00000000..8e3a6ba5 Binary files /dev/null and b/assets/cover-background-f6446981.jpg differ diff --git a/assets/cover-bdee11bd.png b/assets/cover-bdee11bd.png new file mode 100644 index 00000000..7bc35c7a Binary files /dev/null and b/assets/cover-bdee11bd.png differ diff --git a/assets/cover-c3bb2266.png b/assets/cover-c3bb2266.png new file mode 100644 index 00000000..8f4cf499 Binary files /dev/null and b/assets/cover-c3bb2266.png differ diff --git a/assets/cover-c99f79a0.png b/assets/cover-c99f79a0.png new file mode 100644 index 00000000..7ff7a43a Binary files /dev/null and b/assets/cover-c99f79a0.png differ diff --git a/assets/cover-ca6a18cd.png b/assets/cover-ca6a18cd.png new file mode 100644 index 00000000..ddcdade4 Binary files /dev/null and b/assets/cover-ca6a18cd.png differ diff --git a/assets/cover-cf0ba68f.png b/assets/cover-cf0ba68f.png new file mode 100644 index 00000000..b58f98bb Binary files /dev/null and b/assets/cover-cf0ba68f.png differ diff --git a/assets/credit-card-calc-28bb5562.js b/assets/credit-card-calc-28bb5562.js new file mode 100644 index 00000000..dcd28801 --- /dev/null +++ b/assets/credit-card-calc-28bb5562.js @@ -0,0 +1 @@ +import{d as a,v as o,o as r,a as s,h as e,j as i,A as n,k as d}from"./app-033a95d9.js";const l="/assets/cover-76f3ee6d.png",p={class:"markdown-body"},u=n('

簡明的信用卡回饋計算機,輸入刷卡金額並選擇帳戶資格即可試算回饋金額。

此專案採用 Vue 3 的各種新特性,如 Composition API、 <script setup>,並引入 TypeScript 以獲得無縫的開發體驗。

組件完全以 Tailwind CSS 建構,透過 Dark Mode 的 class strategyVueUseuseDark() 來輕鬆實現深色模式。

',4),h={id:"demo",tabindex:"-1"},g=d("Demo "),m={class:"header-anchor",href:"#demo"},_=e("p",null,[e("a",{href:"https://ngseke.github.io/sinopac-dual-currency-card-calculator/"},"https://ngseke.github.io/sinopac-dual-currency-card-calculator/")],-1),k=e("p",null,"(註:一代神卡已殞落,2022 年後已不再適用此回饋規則)",-1),b=e("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=/sinopac-dual-currency-card-calculator&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),v={title:"Sinopac Dual Currency Card Calc.",briefDescription:"永豐幣倍卡回饋計算機",githubLink:"https://github.com/ngseke/sinopac-dual-currency-card-calculator",demoLink:"https://ngseke.github.io/sinopac-dual-currency-card-calculator/",period:["2021/06","2021/06"],cover:"/img/project-cover/credit-card-calc.png",tags:["Vue","TypeScript","Tailwind CSS","Vite","UI/UX"]},V="",D=a({__name:"credit-card-calc",setup(f,{expose:c}){return c({frontmatter:{title:"Sinopac Dual Currency Card Calc.",briefDescription:"永豐幣倍卡回饋計算機",githubLink:"https://github.com/ngseke/sinopac-dual-currency-card-calculator",demoLink:"https://ngseke.github.io/sinopac-dual-currency-card-calculator/",period:["2021/06","2021/06"],cover:"/img/project-cover/credit-card-calc.png",tags:["Vue","TypeScript","Tailwind CSS","Vite","UI/UX"]},excerpt:void 0}),(y,C)=>{const t=o("icon-link");return r(),s("div",p,[u,e("h2",h,[g,e("a",m,[i(t)])]),_,k,b])}}});export{D as default,V as excerpt,v as frontmatter}; diff --git a/assets/customize-youtube-caption-font-3e0540da.js b/assets/customize-youtube-caption-font-3e0540da.js new file mode 100644 index 00000000..ee86bde0 --- /dev/null +++ b/assets/customize-youtube-caption-font-3e0540da.js @@ -0,0 +1,5 @@ +import{d as a,v as c,o as l,a as p,h as s,j as o,k as n,A as r}from"./app-033a95d9.js";const i="/assets/youtube-cc-menu-f1682830.png",d="/assets/before-485cea7e.jpg",_="/assets/after-8a80ee82.jpg",h={class:"markdown-body"},u={id:"%E8%85%B3%E6%9C%AC%E9%80%A3%E7%B5%90",tabindex:"-1"},f=n("腳本連結 "),m={class:"header-anchor",href:"#%E8%85%B3%E6%9C%AC%E9%80%A3%E7%B5%90"},A=s("p",null,[s("a",{href:"https://gist.github.com/ngseke/8b30050e05703f35c753cd0ac6330028"},"https://gist.github.com/ngseke/8b30050e05703f35c753cd0ac6330028")],-1),y={id:"%E8%AA%AA%E6%98%8E",tabindex:"-1"},E=n("說明 "),g={class:"header-anchor",href:"#%E8%AA%AA%E6%98%8E"},C=r('

YouTube CC 字幕雖然有內建設定字體的功能,然而可用的選項並不多,這些選項也都是基於英文字體。

如果你像我一樣對圓體有莫名的愛好,那你一定不能錯過這個腳本。它可以將 YouTube 的字幕替換成任何你想要的字體,前提是你的電腦要先安裝好該字體。

const fontFamily = '"jf-openhuninn-1.1"'
+//                   ^^^^^^^^^^^^^^^^^
+
const fontFamily = '"jf-openhuninn-1.1"'
+//                   ^^^^^^^^^^^^^^^^^
+

將第 :13 行替換成想要的字體名稱即可,例如本例用的是開源圓體「jf open 粉圓」,注意必須保留裡面的雙引號。

`,5),b={id:"%E7%B5%90%E6%9E%9C",tabindex:"-1"},B=n("結果 "),k={class:"header-anchor",href:"#%E7%B5%90%E6%9E%9C"},x=s("p",null,[s("strong",null,"Before:"),s("br"),s("img",{src:d,alt:""})],-1),D=s("p",null,[s("strong",null,"After:"),s("br"),s("img",{src:_,alt:""})],-1),N={title:"把 YouTube CC 字幕變成粉圓體",date:"2022/08/14",tags:["Tampermonkey"]},V="",Y=a({__name:"customize-youtube-caption-font",setup(j,{expose:t}){return t({frontmatter:{title:"把 YouTube CC 字幕變成粉圓體",date:"2022/08/14",tags:["Tampermonkey"]},excerpt:void 0}),(v,T)=>{const e=c("icon-link");return l(),p("div",h,[s("h2",u,[f,s("a",m,[o(e)])]),A,s("h2",y,[E,s("a",g,[o(e)])]),C,s("h2",b,[B,s("a",k,[o(e)])]),x,D])}}});export{Y as default,V as excerpt,N as frontmatter}; diff --git a/assets/dahezhao-4534134f.jpg b/assets/dahezhao-4534134f.jpg new file mode 100644 index 00000000..92067d9c Binary files /dev/null and b/assets/dahezhao-4534134f.jpg differ diff --git a/assets/dashboard-cc7d8fff.png b/assets/dashboard-cc7d8fff.png new file mode 100644 index 00000000..0bd9f45d Binary files /dev/null and b/assets/dashboard-cc7d8fff.png differ diff --git a/assets/datoutie1-f730def3.png b/assets/datoutie1-f730def3.png new file mode 100644 index 00000000..05c578e7 Binary files /dev/null and b/assets/datoutie1-f730def3.png differ diff --git a/assets/datoutie2-b626c0ae.png b/assets/datoutie2-b626c0ae.png new file mode 100644 index 00000000..c801b0b3 Binary files /dev/null and b/assets/datoutie2-b626c0ae.png differ diff --git a/assets/datoutie3-99933bc0.png b/assets/datoutie3-99933bc0.png new file mode 100644 index 00000000..64af4549 Binary files /dev/null and b/assets/datoutie3-99933bc0.png differ diff --git a/assets/datoutie4-8d7f399d.png b/assets/datoutie4-8d7f399d.png new file mode 100644 index 00000000..d93ee40f Binary files /dev/null and b/assets/datoutie4-8d7f399d.png differ diff --git a/assets/datoutie_fb-b37840ad.png b/assets/datoutie_fb-b37840ad.png new file mode 100644 index 00000000..aeb8a479 Binary files /dev/null and b/assets/datoutie_fb-b37840ad.png differ diff --git a/assets/demo-222ae85f.gif b/assets/demo-222ae85f.gif new file mode 100644 index 00000000..91704d63 Binary files /dev/null and b/assets/demo-222ae85f.gif differ diff --git a/assets/deployment-f152146e.png b/assets/deployment-f152146e.png new file mode 100644 index 00000000..7c5fe1cb Binary files /dev/null and b/assets/deployment-f152146e.png differ diff --git a/assets/edit-form-5c88d403.png b/assets/edit-form-5c88d403.png new file mode 100644 index 00000000..1533f381 Binary files /dev/null and b/assets/edit-form-5c88d403.png differ diff --git a/assets/em-optimization-lab-d89b3c86.js b/assets/em-optimization-lab-d89b3c86.js new file mode 100644 index 00000000..c588cf1d --- /dev/null +++ b/assets/em-optimization-lab-d89b3c86.js @@ -0,0 +1 @@ +import{d as a,v as i,o as r,a as c,h as t,j as s,k as o,A as p}from"./app-033a95d9.js";const m="/assets/cover-cf0ba68f.png",_="/assets/login-7406d8e1.png",h="/assets/manage-thesis-1289eceb.png",d="/assets/manage-research-b7b5b46f.png",g="/assets/manage-member-9cbfc70b.png",b="/assets/manage-carousel-5d0aae2a.png",l={class:"markdown-body"},u=t("p",null,"這是台北科技大學之電磁最佳化實驗室 (EM Optimization Lab) 的網站,包含 RWD 來增進不同裝置上的瀏覽體驗。",-1),f=t("p",null,"前端主要以 Vue 架構,使用 Sass 和 Pug 搭配 Gulp 自動化流程工具編譯,使用 Firebase 作為資料庫。",-1),E=t("p",null,[t("img",{src:m,alt:"首頁"})],-1),k={id:"%E5%BE%8C%E5%8F%B0%E7%AE%A1%E7%90%86",tabindex:"-1"},w=o("後台管理 "),y={class:"header-anchor",href:"#%E5%BE%8C%E5%8F%B0%E7%AE%A1%E7%90%86"},v=p('

網站提供了簡單易用的後台介面,讓管理者能方便快速地更新頁面內容,如修改首頁近期活動上傳輪播照片修改實驗室成員 等。

考慮到網站部署空間的限制,因為學校提供的上傳空間只支援靜態網頁,所以資料庫選擇使用 Firebase,其最大的特色就是只靠靜態的前端網頁就可以操作 database。此外它也可以靠 JS 來上傳檔案到儲存空間,也提供驗證登入的功能,讓擁有權限的使用者才可進入後台編輯。

登入後台
後台介面
編輯研究項目
編輯實驗室成員
上傳輪播照片(可拖曳排序)

',3),x={id:"demo",tabindex:"-1"},B=o("Demo "),L={class:"header-anchor",href:"#demo"},V=t("p",null,[t("a",{href:"https://myweb.ntut.edu.tw/~yschen/"},"https://myweb.ntut.edu.tw/~yschen/")],-1),F=t("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=emo&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),D={title:"EM Optimization Lab",briefDescription:"《電磁最佳化實驗室》網站",githubLink:"https://github.com/ngseke/emo",demoLink:"https://myweb.ntut.edu.tw/~yschen/",period:["2018/03","2018/10"],cover:"/img/project-cover/emo.png",tags:["Vue","Firebase","gulp.js","UI/UX"]},N="",U=a({__name:"em-optimization-lab",setup(j,{expose:n}){return n({frontmatter:{title:"EM Optimization Lab",briefDescription:"《電磁最佳化實驗室》網站",githubLink:"https://github.com/ngseke/emo",demoLink:"https://myweb.ntut.edu.tw/~yschen/",period:["2018/03","2018/10"],cover:"/img/project-cover/emo.png",tags:["Vue","Firebase","gulp.js","UI/UX"]},excerpt:void 0}),(A,z)=>{const e=i("icon-link");return r(),c("div",l,[u,f,E,t("h2",k,[w,t("a",y,[s(e)])]),v,t("h2",x,[B,t("a",L,[s(e)])]),V,F])}}});export{U as default,N as excerpt,D as frontmatter}; diff --git a/assets/emo-5557c288.js b/assets/emo-5557c288.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/emo-5557c288.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/eslint-automatically-fixable-4b63f152.png b/assets/eslint-automatically-fixable-4b63f152.png new file mode 100644 index 00000000..422e14ef Binary files /dev/null and b/assets/eslint-automatically-fixable-4b63f152.png differ diff --git a/assets/facebook-messenger-bf65dd89.png b/assets/facebook-messenger-bf65dd89.png new file mode 100644 index 00000000..93d4103d Binary files /dev/null and b/assets/facebook-messenger-bf65dd89.png differ diff --git a/assets/fb-cover-6fb81625.png b/assets/fb-cover-6fb81625.png new file mode 100644 index 00000000..696fdf82 Binary files /dev/null and b/assets/fb-cover-6fb81625.png differ diff --git a/assets/fb_zhuanye-8a3106ac.png b/assets/fb_zhuanye-8a3106ac.png new file mode 100644 index 00000000..68636f6c Binary files /dev/null and b/assets/fb_zhuanye-8a3106ac.png differ diff --git a/assets/fe-e736b23f.js b/assets/fe-e736b23f.js new file mode 100644 index 00000000..c22f85c7 --- /dev/null +++ b/assets/fe-e736b23f.js @@ -0,0 +1 @@ +import{d as t,g as a,B as s,C as r,o as c,a as u,m as e}from"./app-033a95d9.js";const p=t({__name:"fe",setup(l){const{isDownplayed:o}=a();o.value=!0;const n=s();return r(()=>{n.replace({name:"index"})}),(f,i)=>(c(),u("div"))}});typeof e=="function"&&e(p);export{p as default}; diff --git a/assets/flag-5557c288.js b/assets/flag-5557c288.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/flag-5557c288.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/forms-d0d210e4.png b/assets/forms-d0d210e4.png new file mode 100644 index 00000000..9b210bc5 Binary files /dev/null and b/assets/forms-d0d210e4.png differ diff --git a/assets/game1-d689a75e.png b/assets/game1-d689a75e.png new file mode 100644 index 00000000..e0562c65 Binary files /dev/null and b/assets/game1-d689a75e.png differ diff --git a/assets/game2-ec9b0a6e.png b/assets/game2-ec9b0a6e.png new file mode 100644 index 00000000..465bc8bd Binary files /dev/null and b/assets/game2-ec9b0a6e.png differ diff --git a/assets/game3-ae563b09.png b/assets/game3-ae563b09.png new file mode 100644 index 00000000..1f2ded0a Binary files /dev/null and b/assets/game3-ae563b09.png differ diff --git a/assets/game4-ad0db896.png b/assets/game4-ad0db896.png new file mode 100644 index 00000000..f47206fa Binary files /dev/null and b/assets/game4-ad0db896.png differ diff --git a/assets/go-to-file-32915399.png b/assets/go-to-file-32915399.png new file mode 100644 index 00000000..edf83601 Binary files /dev/null and b/assets/go-to-file-32915399.png differ diff --git a/assets/gomoku-e312c04b.js b/assets/gomoku-e312c04b.js new file mode 100644 index 00000000..d4716c8b --- /dev/null +++ b/assets/gomoku-e312c04b.js @@ -0,0 +1 @@ +import{d as s,v as i,o as r,a as c,h as e,j as n,A as g,k as m}from"./app-033a95d9.js";const p="data:image/png;base64,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",a="/assets/register-0126b216.png",u="/assets/room-f8da92ed.png",d="/assets/cover-bdee11bd.png",k="/assets/mobile-bc89f109.png",h={class:"markdown-body"},E=g('

Gomoku: 五子棋對戰是簡約的五子棋遊戲,是與小夥伴在悠閒午後消磨時間的良伴。

註冊畫面
大廳房間列表

遊戲對戰與聊天室

行動裝置版本

',5),U={id:"demo",tabindex:"-1"},I=m("Demo "),A={class:"header-anchor",href:"#demo"},b=e("p",null,[e("a",{href:"https://gomoku.ngseke.me"},"https://gomoku.ngseke.me")],-1),f=e("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=gomoku&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),S={title:"Gomoku",briefDescription:"線上五子棋對戰",githubLink:"https://github.com/ngseke/gomoku",demoLink:"https://gomoku.ngseke.me",period:["2018/10","2018/11"],cover:"/img/project-cover/gomoku.png",tags:["Vue","Firebase","UI/UX"]},R="",X=s({__name:"gomoku",setup(l,{expose:o}){return o({frontmatter:{title:"Gomoku",briefDescription:"線上五子棋對戰",githubLink:"https://github.com/ngseke/gomoku",demoLink:"https://gomoku.ngseke.me",period:["2018/10","2018/11"],cover:"/img/project-cover/gomoku.png",tags:["Vue","Firebase","UI/UX"]},excerpt:void 0}),(F,Q)=>{const t=i("icon-link");return r(),c("div",h,[E,e("h2",U,[I,e("a",A,[n(t)])]),b,f])}}});export{X as default,R as excerpt,S as frontmatter}; diff --git a/assets/hengfu-324e5744.png b/assets/hengfu-324e5744.png new file mode 100644 index 00000000..c565f2cf Binary files /dev/null and b/assets/hengfu-324e5744.png differ diff --git a/assets/hover-type-d5f8cd06.png b/assets/hover-type-d5f8cd06.png new file mode 100644 index 00000000..e8cbce76 Binary files /dev/null and b/assets/hover-type-d5f8cd06.png differ diff --git a/assets/icon-3f390287.png b/assets/icon-3f390287.png new file mode 100644 index 00000000..c4b1c866 Binary files /dev/null and b/assets/icon-3f390287.png differ diff --git a/assets/index-4cd242c9.js b/assets/index-4cd242c9.js new file mode 100644 index 00000000..018bd6d0 --- /dev/null +++ b/assets/index-4cd242c9.js @@ -0,0 +1 @@ +import{d as r,a3 as m,o as t,a as s,h as e,F as _,i as p,t as d,j as u,u as x,a4 as f,m as a}from"./app-033a95d9.js";const g={class:"container px-4 pt-16"},h={class:"mx-auto max-w-3xl py-8 sm:py-16"},k=e("h1",{class:"mb-12 text-4xl font-semibold dark:text-ngsek md:text-5xl"}," ngseke's Blog ",-1),y={class:"flex items-center text-3xl font-medium opacity-70 lg:absolute lg:-left-6 lg:-translate-x-full"},b=r({__name:"index",setup(B){const{postGroups:o}=m();return(v,F)=>{const n=f;return t(),s("div",g,[e("div",h,[k,(t(!0),s(_,null,p(x(o),({name:l,posts:c},i)=>(t(),s("section",{key:i,class:"relative mb-16 space-y-8"},[e("h2",y,d(l),1),u(n,{list:c},null,8,["list"])]))),128))])])}}});typeof a=="function"&&a(b);export{b as default}; diff --git a/assets/index-95e78139.js b/assets/index-95e78139.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/index-95e78139.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/index-f96aa53f.css b/assets/index-f96aa53f.css new file mode 100644 index 00000000..7102aa72 --- /dev/null +++ b/assets/index-f96aa53f.css @@ -0,0 +1 @@ +#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .spinner,.nprogress-custom-parent #nprogress .bar{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0)}to{transform:rotate(360deg)}}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Barlow Semi Condensed,Noto Sans TC,sans-serif;font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Fira Code,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}body{touch-action:manipulation;--tw-text-opacity: 1;color:rgb(23 23 23 / var(--tw-text-opacity))}:is(.dark body){--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(245 245 245 / var(--tw-text-opacity))}code,kbd,samp,pre{font-family:Fira Code,monospace}p{letter-spacing:.2px}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%;margin-right:auto;margin-left:auto}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.link-effect{position:relative}.link-effect:after{pointer-events:none;position:absolute;z-index:-1;--tw-scale-x: .75;--tw-scale-y: .75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));opacity:0;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);content:var(--tw-content);transition-duration:.2s}@media (hover: hover) and (pointer: fine){.link-effect:hover:after{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));content:var(--tw-content);opacity:1}}.link-effect:active{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.link-effect:active:after{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));content:var(--tw-content);opacity:1}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-4{top:1rem;right:1rem;bottom:1rem;left:1rem}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-1\/2{top:50%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.col-auto{grid-column:auto}.\!m-0{margin:0!important}.-mx-4{margin-left:-1rem;margin-right:-1rem}.mx-0{margin-left:0;margin-right:0}.mx-auto{margin-left:auto;margin-right:auto}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.\!mb-2{margin-bottom:.5rem!important}.\!mt-0{margin-top:0!important}.\!mt-10{margin-top:2.5rem!important}.-mt-2{margin-top:-.5rem}.mb-12{margin-bottom:3rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-3{margin-right:.75rem}.mr-5{margin-right:1.25rem}.mr-6{margin-right:1.5rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-\[12\.1rem\]{height:12.1rem}.h-\[5\.5rem\]{height:5.5rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-\[600px\]{max-height:600px}.max-h-full{max-height:100%}.min-h-\[8rem\]{min-height:8rem}.min-h-screen{min-height:100vh}.w-1\/12{width:8.333333%}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-1\/4{width:25%}.w-10{width:2.5rem}.w-3\/5{width:60%}.w-4\/5{width:80%}.w-5{width:1.25rem}.w-8{width:2rem}.w-\[17\.6rem\]{width:17.6rem}.w-\[5rem\]{width:5rem}.w-\[8rem\]{width:8rem}.w-full{width:100%}.min-w-\[1\.5rem\]{min-width:1.5rem}.min-w-\[50\%\]{min-width:50%}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-\[1200px\]{max-width:1200px}.max-w-\[250px\]{max-width:250px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-none{flex:none}.grow{flex-grow:1}.origin-left{transform-origin:left}.origin-top-left{transform-origin:top left}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-45{--tw-rotate: -45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate: 45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-\[-4deg\]{--tw-rotate: -4deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x: 0;--tw-scale-y: 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-75{--tw-scale-x: .75;--tw-scale-y: .75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x: .9;--tw-scale-y: .9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.list-outside{list-style-position:outside}.\!list-none{list-style-type:none!important}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-3{gap:.75rem}.gap-8{gap:2rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1.5rem * var(--tw-space-x-reverse));margin-left:calc(1.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-14>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(3.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(3.5rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-nowrap{white-space:nowrap}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-\[1rem\]{border-bottom-width:1rem}.border-l-4{border-left-width:4px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-dotted{border-style:dotted}.border-black-200{--tw-border-opacity: 1;border-color:rgb(229 229 229 / var(--tw-border-opacity))}.border-black-300{--tw-border-opacity: 1;border-color:rgb(212 212 212 / var(--tw-border-opacity))}.border-black-500\/50{border-color:#73737380}.border-black-800{--tw-border-opacity: 1;border-color:rgb(38 38 38 / var(--tw-border-opacity))}.border-current{border-color:currentColor}.border-ngsek{--tw-border-opacity: 1;border-color:rgb(255 208 25 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity))}.bg-black-100{--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity))}.bg-black-200{--tw-bg-opacity: 1;background-color:rgb(229 229 229 / var(--tw-bg-opacity))}.bg-black-400\/10{background-color:#a3a3a31a}.bg-black-400\/20{background-color:#a3a3a333}.bg-black-900{--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity))}.bg-black-900\/20{background-color:#17171733}.bg-lime-500{--tw-bg-opacity: 1;background-color:rgb(132 204 22 / var(--tw-bg-opacity))}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-sky-500{--tw-bg-opacity: 1;background-color:rgb(14 165 233 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-white\/95{background-color:#fffffff2}.bg-cover{background-size:cover}.bg-center{background-position:center}.object-cover{-o-object-fit:cover;object-fit:cover}.\!p-0{padding:0!important}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-20{padding-left:5rem;padding-right:5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.px-\[\.4rem\]{padding-left:.4rem;padding-right:.4rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-14{padding-bottom:3.5rem}.pb-4{padding-bottom:1rem}.pb-8{padding-bottom:2rem}.pl-1{padding-left:.25rem}.pl-5{padding-left:1.25rem}.pt-10{padding-top:2.5rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-center{text-align:center}.align-middle{vertical-align:middle}.font-mono{font-family:Fira Code,monospace}.font-pacifico{font-family:Pacifico,cursive}.\!text-lg{font-size:1.125rem!important;line-height:1.75rem!important}.\!text-sm{font-size:.875rem!important;line-height:1.25rem!important}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-6xl{font-size:3.75rem;line-height:1}.text-8xl{font-size:6rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.\!font-medium{font-weight:500!important}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.not-italic{font-style:normal}.\!leading-4{line-height:1rem!important}.\!leading-5{line-height:1.25rem!important}.leading-7{line-height:1.75rem}.leading-loose{line-height:2}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-\[\.2px\]{letter-spacing:.2px}.tracking-\[1px\]{letter-spacing:1px}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-black-400{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity))}.text-black-700{--tw-text-opacity: 1;color:rgb(64 64 64 / var(--tw-text-opacity))}.text-black-900\/80{color:#171717cc}.text-ngsek{--tw-text-opacity: 1;color:rgb(255 208 25 / var(--tw-text-opacity))}.text-rose-600{--tw-text-opacity: 1;color:rgb(225 29 72 / var(--tw-text-opacity))}.text-sky-600{--tw-text-opacity: 1;color:rgb(2 132 199 / var(--tw-text-opacity))}.text-teal-600{--tw-text-opacity: 1;color:rgb(13 148 136 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.text-white\/80{color:#fffc}.underline{text-decoration-line:underline}.decoration-dotted{text-decoration-style:dotted}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.delay-200{transition-delay:.2s}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-700{transition-duration:.7s}.duration-75{transition-duration:75ms}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}#nprogress .bar{--tw-bg-opacity: 1;background-color:rgb(255 208 25 / var(--tw-bg-opacity))}#nprogress .peg{box-shadow:0 0 20px #ffd019,0 0 10px #ffd019,0 0 5px #ffd019}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:left-0:before{content:var(--tw-content);left:0}.before\:top-0:before{content:var(--tw-content);top:0}.before\:-z-10:before{content:var(--tw-content);z-index:-10}.before\:-mt-20:before{content:var(--tw-content);margin-top:-5rem}.before\:mr-1:before{content:var(--tw-content);margin-right:.25rem}.before\:block:before{content:var(--tw-content);display:block}.before\:inline-block:before{content:var(--tw-content);display:inline-block}.before\:h-20:before{content:var(--tw-content);height:5rem}.before\:h-full:before{content:var(--tw-content);height:100%}.before\:w-full:before{content:var(--tw-content);width:100%}.before\:min-w-\[\.5rem\]:before{content:var(--tw-content);min-width:.5rem}.before\:origin-right:before{content:var(--tw-content);transform-origin:right}.before\:rounded-xl:before{content:var(--tw-content);border-radius:.75rem}.before\:bg-black-100:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(245 245 245 / var(--tw-bg-opacity))}.before\:text-center:before{content:var(--tw-content);text-align:center}.before\:opacity-50:before{content:var(--tw-content);opacity:.5}.before\:content-\[\"•\"\]:before{--tw-content: "•";content:var(--tw-content)}.before\:content-\[\"▸\"\]:before{--tw-content: "▸";content:var(--tw-content)}.before\:content-\[\'\#\'\]:before{--tw-content: "#";content:var(--tw-content)}.before\:content-\[\'\'\]:before{--tw-content: "";content:var(--tw-content)}.before\:content-\[\'▸\'\]:before{--tw-content: "▸";content:var(--tw-content)}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-3:after{content:var(--tw-content);top:-.75rem;right:-.75rem;bottom:-.75rem;left:-.75rem}.after\:inset-\[-\.3rem\]:after{content:var(--tw-content);top:-.3rem;right:-.3rem;bottom:-.3rem;left:-.3rem}.after\:-inset-x-2:after{content:var(--tw-content);left:-.5rem;right:-.5rem}.after\:-inset-x-3:after{content:var(--tw-content);left:-.75rem;right:-.75rem}.after\:-inset-y-1:after{content:var(--tw-content);top:-.25rem;bottom:-.25rem}.after\:-inset-y-2:after{content:var(--tw-content);top:-.5rem;bottom:-.5rem}.after\:inset-x-\[-1px\]:after{content:var(--tw-content);left:-1px;right:-1px}.after\:inset-x-\[-8px\]:after{content:var(--tw-content);left:-8px;right:-8px}.after\:inset-y-\[-6px\]:after{content:var(--tw-content);top:-6px;bottom:-6px}.after\:-bottom-8:after{content:var(--tw-content);bottom:-2rem}.after\:bottom-\[10\%\]:after{content:var(--tw-content);bottom:10%}.after\:left-0:after{content:var(--tw-content);left:0}.after\:top-0:after{content:var(--tw-content);top:0}.after\:-z-10:after{content:var(--tw-content);z-index:-10}.after\:-z-20:after{content:var(--tw-content);z-index:-20}.after\:block:after{content:var(--tw-content);display:block}.after\:h-8:after{content:var(--tw-content);height:2rem}.after\:h-\[20\%\]:after{content:var(--tw-content);height:20%}.after\:h-full:after{content:var(--tw-content);height:100%}.after\:w-full:after{content:var(--tw-content);width:100%}.after\:scale-\[97\%\]:after{content:var(--tw-content);--tw-scale-x: 97%;--tw-scale-y: 97%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.after\:overflow-hidden:after{content:var(--tw-content);overflow:hidden}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:rounded-lg:after{content:var(--tw-content);border-radius:.5rem}.after\:rounded-xl:after{content:var(--tw-content);border-radius:.75rem}.after\:bg-black-400\/10:after{content:var(--tw-content);background-color:#a3a3a31a}.after\:bg-ngsek\/60:after{content:var(--tw-content);background-color:#ffd01999}.after\:bg-gradient-to-b:after{content:var(--tw-content);background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.after\:from-white:after{content:var(--tw-content);--tw-gradient-from: #fff var(--tw-gradient-from-position);--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.after\:to-\[transparent_70\%\]:after{content:var(--tw-content);--tw-gradient-to: transparent 70% var(--tw-gradient-to-position)}.after\:bg-cover:after{content:var(--tw-content);background-size:cover}.after\:bg-center:after{content:var(--tw-content);background-position:center}.after\:pt-\[56\.25\%\]:after{content:var(--tw-content);padding-top:56.25%}.after\:text-transparent:after{content:var(--tw-content);color:transparent}.after\:opacity-0:after{content:var(--tw-content);opacity:0}.after\:opacity-40:after{content:var(--tw-content);opacity:.4}.after\:blur-lg:after{content:var(--tw-content);--tw-blur: blur(16px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:transition-opacity:after{content:var(--tw-content);transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:duration-200:after{content:var(--tw-content);transition-duration:.2s}.after\:duration-500:after{content:var(--tw-content);transition-duration:.5s}.after\:content-\[\'ngseke\'\]:after{--tw-content: "ngseke";content:var(--tw-content)}.after\:\[background-image\:var\(--img\)\]:after{content:var(--tw-content);background-image:var(--img)}.after\:\[will-change\:filter\]:after{content:var(--tw-content);will-change:filter}@media (hover: hover) and (pointer: fine){.hover\:bg-black-400\/20:hover{background-color:#a3a3a333}.hover\:after\:scale-100:hover:after{content:var(--tw-content);--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:after\:opacity-100:hover:after{content:var(--tw-content);opacity:1}}:is(.dark .dark\:border-black-700){--tw-border-opacity: 1;border-color:rgb(64 64 64 / var(--tw-border-opacity))}:is(.dark .dark\:border-black-800){--tw-border-opacity: 1;border-color:rgb(38 38 38 / var(--tw-border-opacity))}:is(.dark .dark\:bg-black-800){--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-black-900){--tw-bg-opacity: 1;background-color:rgb(23 23 23 / var(--tw-bg-opacity))}:is(.dark .dark\:bg-black-900\/95){background-color:#171717f2}:is(.dark .dark\:text-black-300){--tw-text-opacity: 1;color:rgb(212 212 212 / var(--tw-text-opacity))}:is(.dark .dark\:text-black-400){--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity))}:is(.dark .dark\:text-ngsek){--tw-text-opacity: 1;color:rgb(255 208 25 / var(--tw-text-opacity))}:is(.dark .dark\:text-white\/80){color:#fffc}:is(.dark .dark\:before\:bg-black-800):before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(38 38 38 / var(--tw-bg-opacity))}:is(.dark .dark\:after\:bg-ngsek\/30):after{content:var(--tw-content);background-color:#ffd0194d}:is(.dark .dark\:after\:from-black-900):after{content:var(--tw-content);--tw-gradient-from: #171717 var(--tw-gradient-from-position);--tw-gradient-to: rgb(23 23 23 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}@media print{.print\:hidden{display:none}}@media (min-width: 640px){.sm\:static{position:static}.sm\:right-auto{right:auto}.sm\:top-auto{top:auto}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:h-16{height:4rem}.sm\:\!w-0{width:0px!important}.sm\:w-24{width:6rem}.sm\:w-auto{width:auto}.sm\:flex-none{flex:none}.sm\:scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:transform-none{transform:none}.sm\:justify-start{justify-content:flex-start}.sm\:py-16{padding-top:4rem;padding-bottom:4rem}.sm\:text-6xl{font-size:3.75rem;line-height:1}}@media (min-width: 768px){.md\:w-1\/2{width:50%}.md\:flex-1{flex:1 1 0%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:pt-16{padding-top:4rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-5xl{font-size:3rem;line-height:1}.md\:text-7xl{font-size:4.5rem;line-height:1}.md\:leading-tight{line-height:1.25}}@media (min-width: 1024px){.lg\:absolute{position:absolute}.lg\:-left-6{left:-1.5rem}.lg\:w-full{width:100%}.lg\:flex-none{flex:none}.lg\:-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}}.neon[data-v-b1cdf682]{color:#fff;text-shadow:0 0 6px rgba(255,255,255,.92),0 0 30px rgba(255,255,255,.34),0 0 12px rgba(255,208,25,.52),0 0 21px rgba(255,208,25,.5),0 0 34px rgba(255,208,25,.7),0 0 54px rgba(255,208,25,.5)}.blink[data-v-b1cdf682]:after{text-shadow:0 0 54px rgba(255,208,25,.3);-webkit-animation:blink-b1cdf682 .08s ease-in-out infinite alternate;animation:blink-b1cdf682 .08s ease-in-out infinite alternate}@-webkit-keyframes blink-b1cdf682{0%{opacity:0%}}@keyframes blink-b1cdf682{0%{opacity:0%}} diff --git a/assets/intellisense-e7791f21.png b/assets/intellisense-e7791f21.png new file mode 100644 index 00000000..b72d3844 Binary files /dev/null and b/assets/intellisense-e7791f21.png differ diff --git a/assets/interface3-2a7d9ac4.png b/assets/interface3-2a7d9ac4.png new file mode 100644 index 00000000..62f6356b Binary files /dev/null and b/assets/interface3-2a7d9ac4.png differ diff --git a/assets/interface4-6491aa95.png b/assets/interface4-6491aa95.png new file mode 100644 index 00000000..7dbf73f0 Binary files /dev/null and b/assets/interface4-6491aa95.png differ diff --git a/assets/interface5-fd8930fa.png b/assets/interface5-fd8930fa.png new file mode 100644 index 00000000..377e7c6d Binary files /dev/null and b/assets/interface5-fd8930fa.png differ diff --git a/assets/iphone-price-c267c7d7.js b/assets/iphone-price-c267c7d7.js new file mode 100644 index 00000000..79253dd5 --- /dev/null +++ b/assets/iphone-price-c267c7d7.js @@ -0,0 +1 @@ +import{d as n,v as c,o as r,a as p,h as e,j as o,k as s}from"./app-033a95d9.js";const a="/assets/cover-37fabbf0.png",h="/assets/1-408d5807.png",_="/assets/2-c1925776.png",d="/assets/3-c180c5f6.png",m={class:"markdown-body"},g=e("p",null,[e("img",{src:a,alt:""})],-1),l={id:"%E7%B0%A1%E4%BB%8B",tabindex:"-1"},k=s("簡介 "),f={class:"header-anchor",href:"#%E7%B0%A1%E4%BB%8B"},b=e("p",null,[e("img",{src:h,alt:""}),e("br"),e("img",{src:_,alt:""}),e("br"),e("img",{src:d,alt:""})],-1),u={id:"demo",tabindex:"-1"},B=s("Demo "),v={class:"header-anchor",href:"#demo"},x=e("p",null,[e("a",{href:"https://iphone-price.ngseke.me/"},"https://iphone-price.ngseke.me/")],-1),T={title:"iPhone Price",briefDescription:"台灣 iPhone 價格歷史趨勢",githubLink:"https://github.com/ngseke/iphone-price",demoLink:"https://iphone-price.ngseke.me/",period:["2023/10","2023/10"],cover:"/img/project-cover/iphone-price.png",tags:["Vue","TypeScript","Tailwind","daisyUI","ECharts","UI/UX"]},V="",C=n({__name:"iphone-price",setup(E,{expose:i}){return i({frontmatter:{title:"iPhone Price",briefDescription:"台灣 iPhone 價格歷史趨勢",githubLink:"https://github.com/ngseke/iphone-price",demoLink:"https://iphone-price.ngseke.me/",period:["2023/10","2023/10"],cover:"/img/project-cover/iphone-price.png",tags:["Vue","TypeScript","Tailwind","daisyUI","ECharts","UI/UX"]},excerpt:void 0}),(P,U)=>{const t=c("icon-link");return r(),p("div",m,[g,e("h2",l,[k,e("a",f,[o(t)])]),b,e("h2",u,[B,e("a",v,[o(t)])]),x])}}});export{C as default,V as excerpt,T as frontmatter}; diff --git a/assets/koasu-5245e6bf.js b/assets/koasu-5245e6bf.js new file mode 100644 index 00000000..15164851 --- /dev/null +++ b/assets/koasu-5245e6bf.js @@ -0,0 +1 @@ +import{d as s,v as a,o as i,a as r,h as t,j as n,A as h,k as c}from"./app-033a95d9.js";const p="/assets/cover-c99f79a0.png",u={class:"markdown-body"},g=h('

KOASÛ 也就是台語「歌詞koa-sû」的白話字。

本網誌基於 Hexo 架設,主要受到台客鬼歌詞正字的啟發,補全它們未收錄的台語歌詞,特別是某些非主流歌曲,例如來去高雄。本人將這些歌詞翻譯成符合教育部所規範的台文正字,並以白話字表記發音,也希望藉此過程來提高自己對白話字的掌握度。

台文漢字的選用與拼法主要參考臺灣閩南語常用詞辭典萌典iTaigi 愛台語

',4),k={id:"demo",tabindex:"-1"},d=c("Demo "),m={class:"header-anchor",href:"#demo"},_=t("p",null,[t("a",{href:"https://ngseke.github.io/koasu/"},"https://ngseke.github.io/koasu/")],-1),l=t("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=koasu&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),x={title:"Koasu",briefDescription:"KOASÛ 白話字台語歌詞網誌",githubLink:"https://github.com/ngseke/koasu",demoLink:"https://ngseke.github.io/koasu/",period:"2021/08",cover:"/img/project-cover/koasu.png",tags:["Hexo"]},K="",y=s({__name:"koasu",setup(b,{expose:e}){return e({frontmatter:{title:"Koasu",briefDescription:"KOASÛ 白話字台語歌詞網誌",githubLink:"https://github.com/ngseke/koasu",demoLink:"https://ngseke.github.io/koasu/",period:"2021/08",cover:"/img/project-cover/koasu.png",tags:["Hexo"]},excerpt:void 0}),(f,w)=>{const o=a("icon-link");return i(),r("div",u,[g,t("h2",k,[d,t("a",m,[n(o)])]),_,l])}}});export{y as default,K as excerpt,x as frontmatter}; diff --git a/assets/leetcode-night-b75823ff.js b/assets/leetcode-night-b75823ff.js new file mode 100644 index 00000000..768a4b04 --- /dev/null +++ b/assets/leetcode-night-b75823ff.js @@ -0,0 +1 @@ +import{d as c,v as n,o as l,a,h as e,j as s,k as t,A as r}from"./app-033a95d9.js";const d="/assets/banner-63e3ec60.png",h="/assets/store-f420ca97.png",p="/assets/2-8417bfde.png",g="/assets/1-d295ad8e.png",_={class:"markdown-body"},m=e("p",null,[e("img",{src:d,alt:""})],-1),f=e("p",null,[t("長久以來都只有"),e("a",{href:"https://leetcode-cn.com/"},"中國版力扣"),t("支援完整的暗黑模式,而美國 LeetCode 主站則僅有在 Problem List、Profile 等少數頁面頁才支援,獨缺解題頁。")],-1),u=e("p",null,"某夜在深夜刷題的我突發奇想,說不定我可以取用力扣的色票,直接複寫 LeetCode 的樣式來實現深色模式,於是便誕生了這個專案。",-1),b=e("p",null,[t("原本也僅僅是寫好玩,就把它扔上了 GitHub,甚至連名字都是隨意取的。後來發現其實"),e("a",{href:"https://leetcode.com/discuss/general-discussion/544429/dark-mode"},"蠻多人"),t("都在跟官方敲碗深色模式。因此後來我決定把它上架到商店,讓它變得更容易安裝。對我來說,這也是個難得可以從頭到尾,完整地開發擴充套件的機會。過程除了前端相關的開發、打包、上版號,甚至也包含 UI 和 Logo 設計 、截圖和宣傳橫幅的設計、發布送審套件等。")],-1),k=e("p",null,"經過了半年左右時間,使用者人數終於突破了 1K,還獲得了藍勾 & 精選商品徽章,也算是達成了不值一提的小小成就。",-1),L=e("p",null,[e("img",{src:h,alt:""})],-1),C={id:"%E7%B0%A1%E4%BB%8B",tabindex:"-1"},v=t("簡介 "),B={class:"header-anchor",href:"#%E7%B0%A1%E4%BB%8B"},x=r('

LeetCode Night 是 Google Chrome 的擴充功能,讓使用者在題目頁也可以套用深色模式,即使在夜晚刷題也不必擔心瞎眼。

  • TypeScript
  • UI 組件基於 Tocas UI v4 建構,搭配 styled-component 自定義樣式
  • 使用 Axios 和 GraphQL 請求題目列表
  • 使用 react-i18next 本地化
  • 透過 Webpack css-loader 向頁面注入複寫用的 SASS 樣式

Popup 視窗

此外還有附加以下便利功能:

  1. 反相圖片顏色,讓題目中多數白底的插圖,在深色模式中也不至於太突兀
  2. 輸入 Question Number 來迅速跳轉至題目頁,此插件會將題目列表緩存至本機,無需等待 API 回應
  3. 自動清空編輯器裡前一次送出過的程式碼,特別適合刷題狂人,避免不小心瞄到答案

啟用 LeetCode 深色模式

',6),U={id:"demo",tabindex:"-1"},y=t("Demo "),N={class:"header-anchor",href:"#demo"},S=e("a",{href:"https://chrome.google.com/webstore/detail/leetcode-night/aaokgipfeeeciodnffigjfiafledhcii",target:"_blank"},[e("img",{src:"https://storage.googleapis.com/web-dev-uploads/image/WlD8wC6g8khYWPJUsQceQkhXSlv1/UV4C4ybeBTsZt43U4xis.png"})],-1),w=e("br",null,null,-1),j=e("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=leetcode-night&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),P={title:"LeetCode Night",briefDescription:"LeetCode 深色模式瀏覽器擴充功能",githubLink:"https://github.com/ngseke/leetcode-night",demoLink:"https://chrome.google.com/webstore/detail/leetcode-night/aaokgipfeeeciodnffigjfiafledhcii",period:"2022/02",cover:"/img/project-cover/leetcode-night.png",tags:["React","TypeScript","i18n","styled-components","UI/UX"]},V="",D=c({__name:"leetcode-night",setup(A,{expose:i}){return i({frontmatter:{title:"LeetCode Night",briefDescription:"LeetCode 深色模式瀏覽器擴充功能",githubLink:"https://github.com/ngseke/leetcode-night",demoLink:"https://chrome.google.com/webstore/detail/leetcode-night/aaokgipfeeeciodnffigjfiafledhcii",period:"2022/02",cover:"/img/project-cover/leetcode-night.png",tags:["React","TypeScript","i18n","styled-components","UI/UX"]},excerpt:void 0}),(I,T)=>{const o=n("icon-link");return l(),a("div",_,[m,f,u,b,k,L,e("h2",C,[v,e("a",B,[s(o)])]),x,e("h2",U,[y,e("a",N,[s(o)])]),S,w,j])}}});export{D as default,V as excerpt,P as frontmatter}; diff --git a/assets/legacy-42002bd1.png b/assets/legacy-42002bd1.png new file mode 100644 index 00000000..4b38597f Binary files /dev/null and b/assets/legacy-42002bd1.png differ diff --git a/assets/login-3ac1d3a4.png b/assets/login-3ac1d3a4.png new file mode 100644 index 00000000..541ef1f2 Binary files /dev/null and b/assets/login-3ac1d3a4.png differ diff --git a/assets/login-7406d8e1.png b/assets/login-7406d8e1.png new file mode 100644 index 00000000..b4577b99 Binary files /dev/null and b/assets/login-7406d8e1.png differ diff --git a/assets/logo-animation-3865b365.gif b/assets/logo-animation-3865b365.gif new file mode 100644 index 00000000..24065ebd Binary files /dev/null and b/assets/logo-animation-3865b365.gif differ diff --git a/assets/logo-candidates-4f73c7bd.png b/assets/logo-candidates-4f73c7bd.png new file mode 100644 index 00000000..2e7b7dbd Binary files /dev/null and b/assets/logo-candidates-4f73c7bd.png differ diff --git a/assets/logo_en-8478642b.png b/assets/logo_en-8478642b.png new file mode 100644 index 00000000..dca96e7d Binary files /dev/null and b/assets/logo_en-8478642b.png differ diff --git a/assets/logo_fb-3a5fbf8a.png b/assets/logo_fb-3a5fbf8a.png new file mode 100644 index 00000000..b8d45afa Binary files /dev/null and b/assets/logo_fb-3a5fbf8a.png differ diff --git a/assets/manage-carousel-5d0aae2a.png b/assets/manage-carousel-5d0aae2a.png new file mode 100644 index 00000000..561ddaba Binary files /dev/null and b/assets/manage-carousel-5d0aae2a.png differ diff --git a/assets/manage-member-9cbfc70b.png b/assets/manage-member-9cbfc70b.png new file mode 100644 index 00000000..85293be8 Binary files /dev/null and b/assets/manage-member-9cbfc70b.png differ diff --git a/assets/manage-research-b7b5b46f.png b/assets/manage-research-b7b5b46f.png new file mode 100644 index 00000000..afffdeda Binary files /dev/null and b/assets/manage-research-b7b5b46f.png differ diff --git a/assets/manage-thesis-1289eceb.png b/assets/manage-thesis-1289eceb.png new file mode 100644 index 00000000..c209f4ac Binary files /dev/null and b/assets/manage-thesis-1289eceb.png differ diff --git a/assets/mcip-28b770e5.js b/assets/mcip-28b770e5.js new file mode 100644 index 00000000..3b52193d --- /dev/null +++ b/assets/mcip-28b770e5.js @@ -0,0 +1 @@ +import{d as a,f as r,v as p,o as _,a as d,h as t,j as s,l as h,A as l,k as e}from"./app-033a95d9.js";const m="/assets/1-c36f7e53.png",g="/assets/2-4f25068f.png",u="/assets/3-ff7def49.png",f="/assets/legacy-42002bd1.png",E="/assets/facebook-messenger-bf65dd89.png",b="/assets/deployment-f152146e.png",B="/assets/logo-animation-3865b365.gif",x={class:"markdown-body"},k=l('

樂台計畫官方網站是以 Nuxt 建構的 SSR 形象網站。在此專案我負責的項目,包含定義網站架構與規格、UI/UX 設計、開發與部署。

關於《樂台計畫》平台詳細介紹請見MCIP CMS

Cover
LINE QRCode & Statistics
News & Partners

',3),A={id:"%E7%B7%A3%E7%94%B1",tabindex:"-1"},N=e("緣由 "),S={class:"header-anchor",href:"#%E7%B7%A3%E7%94%B1"},v=t("p",null,"樂台計畫在初期並未規劃官方網站,但隨著規模逐漸成長,有越來越多人與音樂社團對我們感到好奇,這讓我們重新思考品牌形象的建立與加強推廣。為了能接觸到更多群眾,讓更多人可以透過 Facebook 粉絲專頁以外的管道來認識我們,遂開始著手官方網站的構思。",-1),C={id:"%E8%88%8A%E7%89%88%E7%B6%B2%E7%AB%99",tabindex:"-1"},j=e("舊版網站 "),L={class:"header-anchor",href:"#%E8%88%8A%E7%89%88%E7%B6%B2%E7%AB%99"},D=t("p",null,"在原本的前期版本,網站的定位只是一個陽春的入口網站,用來引導使用者加入樂台計畫 LINE App。因此當時是朝著單頁式網站的方向來設計,畫面上也只放置了 QRCode 等必要資訊。",-1),I=t("p",null,"為了盡快的製作出 prototype,當時選用 Parcel 這個易上手的打包工具。只要設定好進入點,它就會自動分析所有相依的資源,並自動封裝成 bundle,也省去很多繁複的手動配置。",-1),V=t("p",null,[t("img",{src:f,alt:"陽春的初版網站首頁"})],-1),P={id:"%E4%BB%A5-nuxt-%E9%87%8D%E6%96%B0%E6%9E%B6%E6%A7%8B",tabindex:"-1"},y=e("以 Nuxt 重新架構 "),M={class:"header-anchor",href:"#%E4%BB%A5-nuxt-%E9%87%8D%E6%96%B0%E6%9E%B6%E6%A7%8B"},U=t("p",null,"隨著網站的內容的不斷擴充,逐漸加入了「合作院校」、「聯絡我們」、「最新消息」和「FAQ」等區塊,我開始思考該如何優化網站 SEO,於是在後期決定採用 Nuxt 這個 SSR 解決方案,從頭開始打造新的網站。",-1),R=t("p",null,"在遷移到 Nuxt 後有許多好處,最顯而易見的就是預先抓取非同步資料,將內容渲染在頁面後才傳送到 client 端。其他像是 title、meta description 和 Open Graph 標籤等,也可以透過 Vue Meta 統一管理,一併預先渲染出來,有助搜尋引擎的爬蟲擷取頁面資料。",-1),O=t("p",null,"例如在社群媒體或是通訊軟體分享最新消息的文章時,縮圖和文章內容就能夠順利的被平台抓取,直接秀出預覽的資訊,提升使用者對連結的興趣。",-1),w=t("p",null,[t("img",{src:E,alt:"Messenger 分享連結預覽"})],-1),G={id:"%E9%83%A8%E7%BD%B2",tabindex:"-1"},Q=e("部署 "),X={class:"header-anchor",href:"#%E9%83%A8%E7%BD%B2"},q=t("p",null,[e("此專案部署在 "),t("a",{href:"https://vercel.com/"},"Vercel"),e(" 上,堪稱是最最無腦的託管平台。基本上只需要按照官方指南,在根目錄建立配置檔 "),t("code",{class:""},"vercel.json"),e(",接著在平台上綁定 GitHub 版本庫即可。")],-1),F=t("p",null,"在預設情況下 Vercel 就內建了 CI/CD,每當任何一個分支有新的 push,它都會立即執行建構和部署,並針對每個 commit 都產生出一個預覽用的 domain,方便驗證線上的結果。",-1),W=t("p",null,[t("img",{src:b,alt:"Vercel Deployment"})],-1),H={id:"landing-page-%E7%9A%84-logo-%E5%8B%95%E7%95%AB",tabindex:"-1"},T=e("Landing Page 的 Logo 動畫 "),$={class:"header-anchor",href:"#landing-page-%E7%9A%84-logo-%E5%8B%95%E7%95%AB"},z=t("p",null,[e("我為識別 Logo 設計了簡單 SVG 動畫,藉著操控 "),t("code",{class:""},"stroke-dashoffset"),e(" 和 "),t("code",{class:""},"stroke-dasharray"),e(" 屬性,僅靠純 CSS3 就能達到酷炫的虛線描邊特效。")],-1),J=t("p",null,[t("img",{src:B,alt:"Logo 動畫示意"})],-1),K={id:"demo",tabindex:"-1"},Y=e("Demo "),Z={class:"header-anchor",href:"#demo"},tt=t("p",null,[t("a",{href:"https://mcip.app/"},"https://mcip.app/")],-1),et=t("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=mcip.app&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),st={id:"related-project",tabindex:"-1"},ot=e("Related Project "),ct={class:"header-anchor",href:"#related-project"},pt={title:"MCIP Official Website",briefDescription:"《樂台計畫》官方網站",githubLink:"https://github.com/ngseke/mcip.ml",demoLink:"https://mcip.app",period:"2019/02",cover:"/img/project-cover/mcip.png",tags:["Nuxt","Schema.org","UI/UX"]},_t="",dt=a({__name:"mcip",setup(nt,{expose:c}){c({frontmatter:{title:"MCIP Official Website",briefDescription:"《樂台計畫》官方網站",githubLink:"https://github.com/ngseke/mcip.ml",demoLink:"https://mcip.app",period:"2019/02",cover:"/img/project-cover/mcip.png",tags:["Nuxt","Schema.org","UI/UX"]},excerpt:void 0});const{projectMap:n}=r(),i=[n.value["mcip-cms"]];return(it,at)=>{const o=p("icon-link");return _(),d("div",x,[k,t("h2",A,[N,t("a",S,[s(o)])]),v,t("h2",C,[j,t("a",L,[s(o)])]),D,I,V,t("h2",P,[y,t("a",M,[s(o)])]),U,R,O,w,t("h2",G,[Q,t("a",X,[s(o)])]),q,F,W,t("h2",H,[T,t("a",$,[s(o)])]),z,J,t("h2",K,[Y,t("a",Z,[s(o)])]),tt,et,t("h2",st,[ot,t("a",ct,[s(o)])]),s(h,{list:i,class:"!mt-10"})])}}});export{dt as default,_t as excerpt,pt as frontmatter}; diff --git a/assets/mcip-cms-dcfd5ebb.js b/assets/mcip-cms-dcfd5ebb.js new file mode 100644 index 00000000..73e3ffcf --- /dev/null +++ b/assets/mcip-cms-dcfd5ebb.js @@ -0,0 +1 @@ +import{d as a,f as r,v as i,o as n,a as m,h as t,j as s,l as _,A as d,k as g}from"./app-033a95d9.js";const l="/assets/fb-cover-6fb81625.png",f="/assets/dashboard-cc7d8fff.png",h="/assets/forms-d0d210e4.png",u="/assets/edit-form-5c88d403.png",b="/assets/login-3ac1d3a4.png",v="/assets/pug-67d261e0.png",V="/assets/competition-1f600c5f.png",j="/assets/config-21adacbc.png",C={class:"markdown-body"},x=d('

樂台計畫誕生於 2018 年之冬,是我與大學好友鎧企(K7)攜手開發的大專院校音樂平台。我們皆來自吉他社,體認到各個賽事在籌辦時的痛點,於是樂台計畫應運而生。樂台計畫專為音樂賽事量身打造,宗旨是建構更優質的賽事環境,簡化社團在處理報名業務的作業流程。

服務上線至今,樂台計畫傾聽來自各方使用者的回饋和建議,持續優化系統與擴展規模。

對於參賽者,樂台計畫以 Line App 官方帳號的形式,讓使用者輕鬆簡單地報名參賽、瀏覽各賽事詳情,甚至可以查看當前比賽進度。目前使用者數已超過 6,000 人,全台從北到南,共計有 26 所大專院校的音樂社團成為我們的合作夥伴。

而對於主辦比賽的社團幹部們,樂台計畫提供一套完整的後台管理系統。在此專案中我主要負責的是後台管理系統前端開發,與一部分 LINE App 的 UI/UX

管理後台概覽頁面(Dashboard)

管理後台大部分組件取自 Vuetify ,得益於這些開箱即用的 Material Design 風格組件,讓我們可以更專注在功能需求上,在初期得以快速地迭代原型。

參賽者報名列表
編輯報名表單

此系統落實前後端分離,使用 Axios 與後端串接資料。我與後端合力規劃並協調 API 的規格,當中包含了 Database Schema 設計。在樂台計畫草創期,我們根據業務邏輯並考慮 NoSQL 的特性,妥善設計出合理的系統架構。
登入畫面

本專案使用 Pug 模板語言撰寫,透過縮排便能很靈活地調整 HTML 的巢狀結構。

專案中使用的 Pug 模板

編輯賽況資訊
設定學校資訊

',12),I={id:"related-project",tabindex:"-1"},M=g("Related Project "),P={class:"header-anchor",href:"#related-project"},D={title:"MCIP CMS",briefDescription:"《樂台計畫》後台管理系統",period:"2018/11",cover:"/img/project-cover/mcip-cms.png",tags:["Vue","Vuetify","ECharts","UI/UX"]},S="",y=a({__name:"mcip-cms",setup(k,{expose:e}){e({frontmatter:{title:"MCIP CMS",briefDescription:"《樂台計畫》後台管理系統",period:"2018/11",cover:"/img/project-cover/mcip-cms.png",tags:["Vue","Vuetify","ECharts","UI/UX"]},excerpt:void 0});const{projectMap:o}=r(),c=[o.value.mcip];return(N,U)=>{const p=i("icon-link");return n(),m("div",C,[x,t("h2",I,[M,t("a",P,[s(p)])]),s(_,{list:c,class:"!mt-10"})])}}});export{y as default,S as excerpt,D as frontmatter}; diff --git a/assets/menu-d3272083.png b/assets/menu-d3272083.png new file mode 100644 index 00000000..4f3f7d9d Binary files /dev/null and b/assets/menu-d3272083.png differ diff --git a/assets/mingpai1-50edfb95.png b/assets/mingpai1-50edfb95.png new file mode 100644 index 00000000..e8c67e10 Binary files /dev/null and b/assets/mingpai1-50edfb95.png differ diff --git a/assets/mingpai2-dead019a.png b/assets/mingpai2-dead019a.png new file mode 100644 index 00000000..6904cda9 Binary files /dev/null and b/assets/mingpai2-dead019a.png differ diff --git a/assets/mobile-bc89f109.png b/assets/mobile-bc89f109.png new file mode 100644 index 00000000..a7b1201f Binary files /dev/null and b/assets/mobile-bc89f109.png differ diff --git a/assets/move-object-properties-99f2f466.gif b/assets/move-object-properties-99f2f466.gif new file mode 100644 index 00000000..28a0ff29 Binary files /dev/null and b/assets/move-object-properties-99f2f466.gif differ diff --git a/assets/open-editors-844d14f7.png b/assets/open-editors-844d14f7.png new file mode 100644 index 00000000..2afc9416 Binary files /dev/null and b/assets/open-editors-844d14f7.png differ diff --git a/assets/output-parserOptions-project-error-ceeaa787.png b/assets/output-parserOptions-project-error-ceeaa787.png new file mode 100644 index 00000000..410e1291 Binary files /dev/null and b/assets/output-parserOptions-project-error-ceeaa787.png differ diff --git a/assets/output-parserOptions-project-without-error-74ff031b.png b/assets/output-parserOptions-project-without-error-74ff031b.png new file mode 100644 index 00000000..4202c4aa Binary files /dev/null and b/assets/output-parserOptions-project-without-error-74ff031b.png differ diff --git a/assets/project-e24afe95.js b/assets/project-e24afe95.js new file mode 100644 index 00000000..ee74041a --- /dev/null +++ b/assets/project-e24afe95.js @@ -0,0 +1 @@ +import{u as J,_ as K,a as Q,b as U}from"./useOgImage-203f60bd.js";import{d as P,o as n,a,i as W,F as y,c as b,w as I,k as T,t as _,b as r,n as X,p as Y,q as c,V as F,e as Z,s as k,v as ee,u as t,j as h,h as g,x as te,y as se,m as H}from"./app-033a95d9.js";import{u as ne}from"./useReadHistory-d98c4b5d.js";const oe={key:2},ae=P({__name:"TeamMembers",props:{list:null},setup(l){const e=o=>o==="黃省喬";return(o,p)=>{const x=X;return n(!0),a(y,null,W(l.list,(u,d)=>{var i;return n(),a(y,{key:d},[e(u)?(n(),b(x,{key:0,class:"underline",href:{name:"index",hash:"#about"}},{default:I(()=>[T(_(u),1)]),_:2},1024)):(n(),a(y,{key:1},[T(_(u),1)],64)),d!==(((i=l.list)==null?void 0:i.length)??0)-1?(n(),a("span",oe,"、")):r("",!0)],64)}),128)}}});function re(){const l=Y();return{frontmatter:c(()=>{var o;return(o=l.meta)==null?void 0:o.frontmatter})}}const ce={class:"space-y-3"},ie={class:"text-4xl font-semibold dark:text-ngsek md:text-5xl"},le={key:0,class:"text-xl font-medium tracking-wide text-black-700 dark:text-black-300"},ue={class:"space-y-1 text-sm text-black-700 dark:text-black-300"},me={key:0},_e=T(" Team Member: "),pe={key:1,class:"space-x-3"},de=P({__name:"project",setup(l){var L,V;const{frontmatter:e}=re(),o=c(()=>{var s;return`${(s=e.value)==null?void 0:s.title} | ${F}`}),p=c(()=>{var s;return(s=e.value)==null?void 0:s.briefDescription}),{ogImageHeadMetaList:x,shouldShowOgImage:u}=J();Z(c(()=>({title:o.value,meta:[{property:"og:site_name",content:F},{property:"og:title",content:o.value},{property:"description",content:p.value},{property:"og:description",content:p.value},...x.value]})));const d=c(()=>{var $;const s=($=e.value)==null?void 0:$.period;if(!s)return null;if(Array.isArray(s)){const[m,f]=s;return m===f?k(m):`${k(m)} - ${k(f)}`}else return`${k(s)} - Present`}),i=c(()=>{var s;return(s=e.value)==null?void 0:s.githubLink}),v=c(()=>{var s;return(s=e.value)==null?void 0:s.demoLink}),{pushReadHistory:A}=ne();return(L=e.value)!=null&&L.name&&A((V=e.value)==null?void 0:V.name),(s,$)=>{var w,D,M;const m=K,f=Q,O=ae,S=te,q=se,G=ee("RouterView"),z=U;return n(),a(y,null,[t(u)?(n(),b(m,{key:0,description:t(p),img:(w=t(e))==null?void 0:w.cover,tags:(D=t(e))==null?void 0:D.tags,title:(M=t(e))==null?void 0:M.title},null,8,["description","img","tags","title"])):r("",!0),h(z,null,{header:I(()=>{var N,R,j,B,C,E;return[g("div",ce,[g("h1",ie,_((N=t(e))==null?void 0:N.title),1),(R=t(e))!=null&&R.briefDescription?(n(),a("p",le,_((j=t(e))==null?void 0:j.briefDescription),1)):r("",!0),h(f,{list:(B=t(e))==null?void 0:B.tags},null,8,["list"]),g("ul",ue,[g("li",null,_(t(d)),1),(C=t(e))!=null&&C.members?(n(),a("li",me,[_e,h(O,{list:(E=t(e))==null?void 0:E.members},null,8,["list"])])):r("",!0)]),t(i)||t(v)?(n(),a("div",pe,[t(i)?(n(),b(S,{key:0,href:t(i)},null,8,["href"])):r("",!0),t(v)?(n(),b(q,{key:1,href:t(v)},null,8,["href"])):r("",!0)])):r("",!0)])]}),default:I(()=>[h(G)]),_:1})],64)}}});typeof H=="function"&&H(de);export{de as default}; diff --git a/assets/projects-34cb3ecc.js b/assets/projects-34cb3ecc.js new file mode 100644 index 00000000..398fc9b2 --- /dev/null +++ b/assets/projects-34cb3ecc.js @@ -0,0 +1 @@ +import{d as p,o as e,c as A,w as l,u as a,a as t,b as f,T as P,_ as m,r as x,e as C,V as d,f as T,g as V,h,F as B,i as N,j as c,k as y,t as R,l as S,m as u}from"./app-033a95d9.js";import{u as E}from"./useReadHistory-d98c4b5d.js";const D=p({__name:"AlertReadAllProject",setup(s){const{isReadAll:o,clearReadHistory:n}=E();return(_,r)=>(e(),A(P,{leaveActiveClass:"transition-all",leaveToClass:"scale-0"},{default:l(()=>[a(o)?(e(),t("div",{key:0,class:"mt-2 origin-left font-mono text-xs",onClick:r[0]||(r[0]=(...i)=>a(n)&&a(n)(...i))}," Wow, you've read all of my projects 😳 ")):f("",!0)]),_:1}))}}),H={},F={class:"link-effect inline-block whitespace-nowrap text-lg font-semibold leading-none after:inset-x-[-8px] after:inset-y-[-6px] after:rounded-lg after:bg-black-400/10",type:"button"};function I(s,o){return e(),t("button",F,[x(s.$slots,"default")])}const L=m(H,[["render",I]]),M={},W={class:"text-4xl font-semibold md:text-5xl"};function q(s,o){return e(),t("h2",W,[x(s.$slots,"default")])}const z=m(M,[["render",q]]),G={class:"container px-4 pt-16"},J={class:"mx-auto max-w-5xl py-16"},K={key:0},O=h("hr",{class:"mb-6 border-dashed border-black-200 dark:border-black-800"},null,-1),Q=y(" Show All Projects "),U=p({__name:"projects",setup(s){C({title:`Projects | ${d}`,meta:[{property:"og:site_name",content:d}]});const{downplayedProjects:o}=T(),{isDownplayed:n}=V();function _(){n.value=!1}return(r,i)=>{const b=z,k=S,g=L,j=D;return e(),t("div",G,[h("div",J,[(e(!0),t(B,null,N(a(o),({title:w,list:$},v)=>(e(),t("section",{key:v,class:"mb-12 space-y-8"},[c(b,null,{default:l(()=>[y(R(w),1)]),_:2},1024),c(k,{list:$},null,8,["list"])]))),128)),a(n)?(e(),t("div",K,[O,c(g,{onClick:_},{default:l(()=>[Q]),_:1})])):f("",!0),c(j)])])}}});typeof u=="function"&&u(U);export{U as default}; diff --git a/assets/pug-67d261e0.png b/assets/pug-67d261e0.png new file mode 100644 index 00000000..06189ed7 Binary files /dev/null and b/assets/pug-67d261e0.png differ diff --git a/assets/raise-your-red-flag-6df7832f.js b/assets/raise-your-red-flag-6df7832f.js new file mode 100644 index 00000000..2e59944d --- /dev/null +++ b/assets/raise-your-red-flag-6df7832f.js @@ -0,0 +1 @@ +import{d as n,v as r,o as i,a as c,h as e,j as o,k as t}from"./app-033a95d9.js";const l="/assets/cover-c3bb2266.png",d={class:"markdown-body"},p=e("p",null,"數位影像處理期末專題。",-1),h=e("p",null,[e("img",{src:l,alt:""})],-1),_=e("p",null,[t("藉助 "),e("strong",null,"tracking.js"),t(" 的力量,在瀏覽器以 Webcam 遊玩,重溫耳熟能詳的團康遊戲,一起成為"),e("em",null,"那支舉起專家"),t("。")],-1),m={id:"%E8%A9%A6%E7%8E%A9%E5%BD%B1%E7%89%87",tabindex:"-1"},g=t("試玩影片 "),u={class:"header-anchor",href:"#%E8%A9%A6%E7%8E%A9%E5%BD%B1%E7%89%87"},b=e("div",{class:"embed-responsive"},[e("iframe",{class:"embed-responsive-item",src:"https://www.youtube.com/embed/du_2fcqPENo",allowfullscreen:""})],-1),f={id:"demo",tabindex:"-1"},k=t("Demo "),v={class:"header-anchor",href:"#demo"},E=e("p",null,[e("a",{href:"https://raise-flag.web.app"},"https://raise-flag.web.app/")],-1),w=e("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=Raise-Your-Red-Flag&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),D={title:"Raise Your Red Flag",briefDescription:"以 Webcam 重現經典團康遊戲《紅旗舉起來》",githubLink:"https://github.com/ngseke/Raise-Your-Red-Flag",demoLink:"https://raise-flag.web.app/",period:["2018/06","2018/06"],cover:"/img/project-cover/flag.png"},F="",Y=n({__name:"raise-your-red-flag",setup(R,{expose:a}){return a({frontmatter:{title:"Raise Your Red Flag",briefDescription:"以 Webcam 重現經典團康遊戲《紅旗舉起來》",githubLink:"https://github.com/ngseke/Raise-Your-Red-Flag",demoLink:"https://raise-flag.web.app/",period:["2018/06","2018/06"],cover:"/img/project-cover/flag.png"},excerpt:void 0}),(x,B)=>{const s=r("icon-link");return i(),c("div",d,[p,h,_,e("h2",m,[g,e("a",u,[o(s)])]),b,e("h2",f,[k,e("a",v,[o(s)])]),E,w])}}});export{Y as default,F as excerpt,D as frontmatter}; diff --git a/assets/react-handler-type-3a8ff22d.js b/assets/react-handler-type-3a8ff22d.js new file mode 100644 index 00000000..e5ac7210 --- /dev/null +++ b/assets/react-handler-type-3a8ff22d.js @@ -0,0 +1,197 @@ +import{d as e,v as c,o as t,a as r,h as s,j as a,k as l,A as p}from"./app-033a95d9.js";const y="/assets/hover-type-d5f8cd06.png",B={class:"markdown-body"},A={id:"tl%3Bdr",tabindex:"-1"},i=l("TL;DR "),D={class:"header-anchor",href:"#tl%3Bdr"},C=p(`

如果某個 event handler 的事件參數,就只是單純拿來呼叫 preventDefault()stopPropagation(),那麼無論它是什麼元素,只要將 event 指定成 SyntheticEvent 型別即可,寫起來也非常簡潔。

import { SyntheticEvent } from 'react' // 記得 import
+
+// ...
+
+const handleSubmit = (event: SyntheticEvent) => {
+  event.preventDefault()
+}
+
+return (
+  <form onSubmit={handleSubmit}>
+    {/* ... */}
+  </form>
+)
+
import { SyntheticEvent } from 'react' // 記得 import
+
+// ...
+
+const handleSubmit = (event: SyntheticEvent) => {
+  event.preventDefault()
+}
+
+return (
+  <form onSubmit={handleSubmit}>
+    {/* ... */}
+  </form>
+)
+
`,2),d={id:"%E5%88%86%E6%9E%90",tabindex:"-1"},E=l("分析 "),F={class:"header-anchor",href:"#%E5%88%86%E6%9E%90"},h=p(`

考慮以下處理表單送出的程式碼:

function App () {
+  return (
+    <form
+      onSubmit={(event) => {
+        event.preventDefault()
+        /* ... */
+      }}
+    >
+      {/* ... */}
+    </form>
+  )
+}
+
function App () {
+  return (
+    <form
+      onSubmit={(event) => {
+        event.preventDefault()
+        /* ... */
+      }}
+    >
+      {/* ... */}
+    </form>
+  )
+}
+

有時候會想把 inline event handler onSubmit 給抽出來,變成 handleSubmit

但直接取出函式當然是行不通的,因為 TypeScript 無從推斷出參數 event 的型別。

function App () {
+  const handleSubmit = (event) => {
+    //                  ^^^^^
+    // Parameter 'event' implicitly has an 'any' type.ts(7006)
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+
function App () {
+  const handleSubmit = (event) => {
+    //                  ^^^^^
+    // Parameter 'event' implicitly has an 'any' type.ts(7006)
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+

在某些情況,如果 handler 裡根本沒用到 event 這個參數,例如常用的 onClick 事件,那麼這時其實把參數直接移除就行。

const handleClick = () => {
+  console.log('Clicked')
+}
+
+return (
+  <button onClick={handleClick}>
+    Button
+  </button>
+)
+
const handleClick = () => {
+  console.log('Clicked')
+}
+
+return (
+  <button onClick={handleClick}>
+    Button
+  </button>
+)
+

然而在處理表單的 onSubmit 的場合時,經常會需要呼叫這個事件的 preventDefault(),用來阻止事件傳遞。

因此我們常常得 「復刻」 出這個事件的型別,而不得不寫出冗長難讀的型別,例如 React.FormEventHandler<HTMLFormElement>

function App () {
+  const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+
function App () {
+  const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+

BTW 這一長串型別的定義,可以透過將游標放在 JSX 事件上得知。

',11),g={id:"synthetic-event",tabindex:"-1"},u=l("Synthetic Event "),f={class:"header-anchor",href:"#synthetic-event"},v=p(`

翻開 React 的官方文件可以得知,在 React 中的所有事件都是 Synthetic Event,而原生的 Event。兩者的 API 雖然長得很相似,但實際上並不是同一個東西。

可以試著追溯 onSubmit 事件的型別來觀察這件事:

// node_modules/@types/react/index.d.ts:1383
+interface DOMAttributes<T> {
+  // ...
+  onSubmit?: FormEventHandler<T> | undefined;
+}
+
// node_modules/@types/react/index.d.ts:1383
+interface DOMAttributes<T> {
+  // ...
+  onSubmit?: FormEventHandler<T> | undefined;
+}
+

再往上查看 FormEventHandlerFormEvent

// node_modules/@types/react/index.d.ts:1303
+type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
+
+// node_modules/@types/react/index.d.ts:1195
+interface FormEvent<T = Element> extends SyntheticEvent<T> {
+}
+
// node_modules/@types/react/index.d.ts:1303
+type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
+
+// node_modules/@types/react/index.d.ts:1195
+interface FormEvent<T = Element> extends SyntheticEvent<T> {
+}
+

由此可知 onSubmit 確實是一層層地從 SyntheticEvent extend 出來的。

可以再進一步查看 SyntheticEvent 型別的定義,即可看到 preventDefault() 方法:

interface SyntheticEvent<T = Element, E = Event>
+  extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
+
+interface BaseSyntheticEvent<E = object, C = any, T = any> {
+  nativeEvent: E;
+  currentTarget: C;
+  target: T;
+  bubbles: boolean;
+  cancelable: boolean;
+  defaultPrevented: boolean;
+  eventPhase: number;
+  isTrusted: boolean;
+  preventDefault(): void;
+  isDefaultPrevented(): boolean;
+  stopPropagation(): void;
+  isPropagationStopped(): boolean;
+  persist(): void;
+  timeStamp: number;
+  type: string;
+}
+
interface SyntheticEvent<T = Element, E = Event>
+  extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
+
+interface BaseSyntheticEvent<E = object, C = any, T = any> {
+  nativeEvent: E;
+  currentTarget: C;
+  target: T;
+  bubbles: boolean;
+  cancelable: boolean;
+  defaultPrevented: boolean;
+  eventPhase: number;
+  isTrusted: boolean;
+  preventDefault(): void;
+  isDefaultPrevented(): boolean;
+  stopPropagation(): void;
+  isPropagationStopped(): boolean;
+  persist(): void;
+  timeStamp: number;
+  type: string;
+}
+
`,8),m={id:"%E7%B5%90%E8%AB%96",tabindex:"-1"},k=l("結論 "),b={class:"header-anchor",href:"#%E7%B5%90%E8%AB%96"},S=p(`

經過分析可以得知,其實以下任一種寫法都可以使 handleSubmit 相容 onSubmit 事件。

const handleSubmit = (e: React.SyntheticEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit = (e: React.SyntheticEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit = (e: React.FormEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit = (e: React.FormEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit: React.FormEventHandler = (e) => {
+  e.preventDefault()
+}
+
const handleSubmit: React.FormEventHandler = (e) => {
+  e.preventDefault()
+}
+
`,4),H={title:"處理在 React 抽出 event handler 時常碰到的 TypeScript 參數型別問題",date:"2022/06/06",tags:["React","TypeScript"]},P="",M=e({__name:"react-handler-type",setup(_,{expose:o}){return o({frontmatter:{title:"處理在 React 抽出 event handler 時常碰到的 TypeScript 參數型別問題",date:"2022/06/06",tags:["React","TypeScript"]},excerpt:void 0}),(T,x)=>{const n=c("icon-link");return t(),r("div",B,[s("h2",A,[i,s("a",D,[a(n)])]),C,s("h2",d,[E,s("a",F,[a(n)])]),h,s("h2",g,[u,s("a",f,[a(n)])]),v,s("h2",m,[k,s("a",b,[a(n)])]),S])}}});export{M as default,P as excerpt,H as frontmatter}; diff --git a/assets/register-0126b216.png b/assets/register-0126b216.png new file mode 100644 index 00000000..87ab3e89 Binary files /dev/null and b/assets/register-0126b216.png differ diff --git a/assets/rename-symbol-ac29a4c0.png b/assets/rename-symbol-ac29a4c0.png new file mode 100644 index 00000000..ac3cc343 Binary files /dev/null and b/assets/rename-symbol-ac29a4c0.png differ diff --git a/assets/reproduce-bootstrap-grid-in-tailwind-ce693ef5.js b/assets/reproduce-bootstrap-grid-in-tailwind-ce693ef5.js new file mode 100644 index 00000000..1b206df6 --- /dev/null +++ b/assets/reproduce-bootstrap-grid-in-tailwind-ce693ef5.js @@ -0,0 +1,37 @@ +import{d as p,v as c,o as t,a as r,h as s,j as a,k as o,A as n}from"./app-033a95d9.js";const d={class:"markdown-body"},i=s("blockquote",null,[s("p",null,[o("此筆記提到的 Grid 指的是元件庫 "),s("a",{href:"https://getbootstrap.com/"},"Bootstrap"),o(" 所提供的的網格系統(Grid system),和 CSS 的 "),s("code",{class:""},"display: grid"),o(" 屬性無關。")])],-1),y=s("blockquote",null,[s("p",null,"轉換後的 class 屬性有稍微簡化,但我想足以覆蓋大部分的使用場景了。")],-1),B={id:"bootstrap-%E5%92%8C-tailwind-%E7%9A%84-class-name-%E5%B0%8D%E6%87%89",tabindex:"-1"},h=o("Bootstrap 和 Tailwind 的 Class Name 對應 "),A={class:"header-anchor",href:"#bootstrap-%E5%92%8C-tailwind-%E7%9A%84-class-name-%E5%B0%8D%E6%87%89"},_={id:"row",tabindex:"-1"},f=s("code",{class:""},"row",-1),u=o(),g={class:"header-anchor",href:"#row"},E=s("p",null,[o("→ "),s("code",{class:""},"flex flex-wrap")],-1),x={id:"col",tabindex:"-1"},b=s("code",{class:""},"col",-1),D=o(),v={class:"header-anchor",href:"#col"},w=n('

flex-1

備註:

.flex-1\n  flex: 1 // 即 `1 1 0%`\n
.flex-1\n  flex: 1 // 即 `1 1 0%`\n
',3),F={id:"col-auto",tabindex:"-1"},C=s("code",{class:""},"col-auto",-1),k=o(),m={class:"header-anchor",href:"#col-auto"},q=n('

flex-none

備註:

.flex-none\n  flex: none // 即 `0 0 auto`\n
.flex-none\n  flex: none // 即 `0 0 auto`\n
',3),T={id:"col-*",tabindex:"-1"},S=s("code",{class:""},"col-*",-1),G=o(),N={class:"header-anchor",href:"#col-*"},V=n('
  • col-1flex-none w-1/12
  • col-3flex-none w-1/4
  • col-4flex-none w-1/3
  • col-6flex-none w-1/2
  • col-12flex-none w-full

有需要的話,你甚至可以在 Tailwind 透過 Arbitrary Value 的特性來指定任意寬度,例如:
w-[5rem]w-[50px]

',2),j={id:"w-100",tabindex:"-1"},R=s("code",{class:""},"w-100",-1),z=o(),M={class:"header-anchor",href:"#w-100"},W=s("p",null,[o("→ "),s("code",{class:""},"w-full")],-1),H=s("p",null,"用於強迫換行。",-1),I={id:"%E9%9F%BF%E6%87%89%E5%BC%8F%EF%BC%88rwd%EF%BC%89",tabindex:"-1"},J=o("響應式(RWD) "),K={class:"header-anchor",href:"#%E9%9F%BF%E6%87%89%E5%BC%8F%EF%BC%88rwd%EF%BC%89"},L=n('

By default, Tailwind uses a mobile first breakpoint system, …
Breakpoints · Bootstrap v5.2

Mobile first, responsive design is the goal. …
Responsive Design - Tailwind CSS

Tailwind 和 Bootstrap 都同樣遵循著行動裝置優先的設計哲學,寬度由小到大。也就是說,當什麼斷點都沒加的時候,樣式會套用到所有寬度的裝置(>= 0px),而加上 sm 時會套用在 >= 640px,再加上 md 時會套用在 >= 768px,依此類推。

值得注意的是,它們兩者斷點的預設值稍微不太一樣,例如 sm 在 Tailwind 是 640px,在 Bootstrap 則是 576px。不過這完全不成問題,因為你可以在 tailwind.config.js 輕鬆的自定義,甚至自創斷點名稱。

轉換範例如下:

  • col-12flex-none w-full
  • col-md-6flex-none md:w-1/2
  • col-3 col-sm-auto col-md col-lg-12
    flex-1 w-1/4 sm:flex-none sm:w-auto md:flex-1 lg:flex-none lg:w-full
',6),O={id:"%E9%96%93%E8%B7%9D%EF%BC%88gutter%EF%BC%89",tabindex:"-1"},P=o("間距(Gutter) "),Q={class:"header-anchor",href:"#%E9%96%93%E8%B7%9D%EF%BC%88gutter%EF%BC%89"},U=n('

在預設情況下,column 之間是沒有間距的。這相當於是套用了 Bootstrap 4 的 row no-gutters,或是 Bootstrap 5 的 row g-0

在 Tailwind 的場合想要實現 gutter,必須另外做以下兩點:

  1. 為 row 元素加上負值的水平 margin,例如 -mx-4
  2. 為每一個 column 元素,加上與 row 元素相對應的水平 padding,例如 px-4

row 的負值 margin 是用來抵銷在最左或最右邊緣的 column 的 padding。

',4),X={id:"%E7%AF%84%E4%BE%8B",tabindex:"-1"},Y=o("範例 "),Z={class:"header-anchor",href:"#%E7%AF%84%E4%BE%8B"},$=n(`
col-12
col-6
col-3
col
col-auto
<div class="-mx-4 flex flex-wrap">
+  <div class="w-full flex-none px-4">
+    col-12
+  </div>
+  <div class="w-1/2 flex-none px-4">
+    col-6
+  </div>
+  <div class="w-1/4 flex-none px-4">
+    col-3
+  </div>
+  <div class="w-full"><!-- 換行用 --></div>
+  <div class="flex-none px-4">
+    col
+  </div>
+  <div class="flex-1 px-4">
+    col-auto
+  </div>
+</div>
+
<div class="-mx-4 flex flex-wrap">
+  <div class="w-full flex-none px-4">
+    col-12
+  </div>
+  <div class="w-1/2 flex-none px-4">
+    col-6
+  </div>
+  <div class="w-1/4 flex-none px-4">
+    col-3
+  </div>
+  <div class="w-full"><!-- 換行用 --></div>
+  <div class="flex-none px-4">
+    col
+  </div>
+  <div class="flex-1 px-4">
+    col-auto
+  </div>
+</div>
+
`,2),ss={id:"%E5%BB%B6%E4%BC%B8%E9%96%B1%E8%AE%80",tabindex:"-1"},os=o("延伸閱讀 "),ls={class:"header-anchor",href:"#%E5%BB%B6%E4%BC%B8%E9%96%B1%E8%AE%80"},as=s("p",null,[s("a",{href:"https://stackoverflow.com/questions/34352140/what-are-the-differences-between-flex-basis-and-width"},[s("code",{class:""},"flex-basis"),o(" 和 "),s("code",{class:""},"width"),o(" 的差別是什麼?")])],-1),ts={title:"用 Tailwind 重現 Bootstrap 的 Grid System",date:"2022/08/22",tags:["Tailwind","Bootstrap"]},rs="",ds=p({__name:"reproduce-bootstrap-grid-in-tailwind",setup(ns,{expose:e}){return e({frontmatter:{title:"用 Tailwind 重現 Bootstrap 的 Grid System",date:"2022/08/22",tags:["Tailwind","Bootstrap"]},excerpt:void 0}),(es,ps)=>{const l=c("icon-link");return t(),r("div",d,[i,y,s("h2",B,[h,s("a",A,[a(l)])]),s("h3",_,[f,u,s("a",g,[a(l)])]),E,s("h3",x,[b,D,s("a",v,[a(l)])]),w,s("h3",F,[C,k,s("a",m,[a(l)])]),q,s("h3",T,[S,G,s("a",N,[a(l)])]),V,s("h3",j,[R,z,s("a",M,[a(l)])]),W,H,s("h2",I,[J,s("a",K,[a(l)])]),L,s("h2",O,[P,s("a",Q,[a(l)])]),U,s("h2",X,[Y,s("a",Z,[a(l)])]),$,s("h2",ss,[os,s("a",ls,[a(l)])]),as])}}});export{ds as default,rs as excerpt,ts as frontmatter}; diff --git a/assets/room-f8da92ed.png b/assets/room-f8da92ed.png new file mode 100644 index 00000000..09cddfb0 Binary files /dev/null and b/assets/room-f8da92ed.png differ diff --git a/assets/screenshot-1-81b8fb35.png b/assets/screenshot-1-81b8fb35.png new file mode 100644 index 00000000..43cad73d Binary files /dev/null and b/assets/screenshot-1-81b8fb35.png differ diff --git a/assets/screenshot-2-9726f1a7.png b/assets/screenshot-2-9726f1a7.png new file mode 100644 index 00000000..bd25b88e Binary files /dev/null and b/assets/screenshot-2-9726f1a7.png differ diff --git a/assets/shanlinliang-9edbff22.js b/assets/shanlinliang-9edbff22.js new file mode 100644 index 00000000..6a89e720 --- /dev/null +++ b/assets/shanlinliang-9edbff22.js @@ -0,0 +1 @@ +import{d as e,o as n,a as o,h as t,k as a}from"./app-033a95d9.js";const r="/assets/cover-ca6a18cd.png",i="/assets/sll1-98c64e6a.png",l="/assets/sll4-b982d4df.png",c="/assets/sll5-a9b2ee83.png",p={class:"markdown-body"},_=t("p",null,[t("strong",null,"《扇林涼 Shanlinliang》"),a(" 是一個虛構的涼扇品牌,運用簡約設計文字營造出商品的高級感。")],-1),g=t("p",null,[t("img",{src:r,alt:""})],-1),d=t("p",null,[t("img",{src:i,alt:""})],-1),m=t("p",null,[t("img",{src:l,alt:""}),t("br"),t("img",{src:c,alt:""})],-1),h=[_,g,d,m],x={title:"Shanlinliang",briefDescription:"虛構涼扇品牌廣告《扇林涼》",period:["2017/10","2017/10"],cover:"/img/project-cover/shanlinliang.png",tags:["Illustrator"]},k="",B=e({__name:"shanlinliang",setup(u,{expose:s}){return s({frontmatter:{title:"Shanlinliang",briefDescription:"虛構涼扇品牌廣告《扇林涼》",period:["2017/10","2017/10"],cover:"/img/project-cover/shanlinliang.png",tags:["Illustrator"]},excerpt:void 0}),(f,v)=>(n(),o("div",p,h))}});export{B as default,k as excerpt,x as frontmatter}; diff --git a/assets/shouce1-193ed0a8.jpg b/assets/shouce1-193ed0a8.jpg new file mode 100644 index 00000000..988a6dcb Binary files /dev/null and b/assets/shouce1-193ed0a8.jpg differ diff --git a/assets/shouce2-0a3f097e.jpg b/assets/shouce2-0a3f097e.jpg new file mode 100644 index 00000000..b11d3af0 Binary files /dev/null and b/assets/shouce2-0a3f097e.jpg differ diff --git a/assets/sll1-98c64e6a.png b/assets/sll1-98c64e6a.png new file mode 100644 index 00000000..4077e5c6 Binary files /dev/null and b/assets/sll1-98c64e6a.png differ diff --git a/assets/sll4-b982d4df.png b/assets/sll4-b982d4df.png new file mode 100644 index 00000000..95689345 Binary files /dev/null and b/assets/sll4-b982d4df.png differ diff --git a/assets/sll5-a9b2ee83.png b/assets/sll5-a9b2ee83.png new file mode 100644 index 00000000..1da10979 Binary files /dev/null and b/assets/sll5-a9b2ee83.png differ diff --git a/assets/store-f420ca97.png b/assets/store-f420ca97.png new file mode 100644 index 00000000..8e407a4d Binary files /dev/null and b/assets/store-f420ca97.png differ diff --git a/assets/tabs-fc5ce7c7.png b/assets/tabs-fc5ce7c7.png new file mode 100644 index 00000000..a3f8e08e Binary files /dev/null and b/assets/tabs-fc5ce7c7.png differ diff --git a/assets/terminal-lint-09df797c.png b/assets/terminal-lint-09df797c.png new file mode 100644 index 00000000..4e9400db Binary files /dev/null and b/assets/terminal-lint-09df797c.png differ diff --git a/assets/tic-tac-toe-dd64aee5.js b/assets/tic-tac-toe-dd64aee5.js new file mode 100644 index 00000000..08129a72 --- /dev/null +++ b/assets/tic-tac-toe-dd64aee5.js @@ -0,0 +1 @@ +import{d as s,v as c,o as i,a as n,h as t,j as a,k as r}from"./app-033a95d9.js";const p="/assets/1-ce441fa9.png",h="/assets/2-6e0e5062.png",_={class:"markdown-body"},g=t("p",null,[t("img",{src:p,alt:""}),t("br"),t("img",{src:h,alt:""})],-1),d=t("p",null,"簡直不可理喻的圈圈叉叉遊戲。在這個星球上同一時間,最多只能供兩個人類遊玩。",-1),l=t("p",null,"有附帶聊天室,提供玩家文明交流的空間。",-1),m={id:"demo",tabindex:"-1"},u=r("Demo "),b={class:"header-anchor",href:"#demo"},k=t("p",null,[t("a",{href:"https://ngseke.github.io/tic-tac-toe/"},"https://ngseke.github.io/tic-tac-toe/")],-1),f=t("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=tic-tac-toe&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),L={title:"Tic Tac Toe",briefDescription:"圈圈叉叉亂鬥",githubLink:"https://github.com/ngseke/tic-tac-toe",demoLink:"https://ngseke.github.io/tic-tac-toe/",period:["2018/10","2018/10"],cover:"/img/project-cover/tic-tac-toe.png",tags:["Vue","gulp","Firebase","UI/UX"]},U="",j=s({__name:"tic-tac-toe",setup(v,{expose:e}){return e({frontmatter:{title:"Tic Tac Toe",briefDescription:"圈圈叉叉亂鬥",githubLink:"https://github.com/ngseke/tic-tac-toe",demoLink:"https://ngseke.github.io/tic-tac-toe/",period:["2018/10","2018/10"],cover:"/img/project-cover/tic-tac-toe.png",tags:["Vue","gulp","Firebase","UI/UX"]},excerpt:void 0}),(T,x)=>{const o=c("icon-link");return i(),n("div",_,[g,d,l,t("h2",m,[u,t("a",b,[a(o)])]),k,f])}}});export{j as default,U as excerpt,L as frontmatter}; diff --git a/assets/tixing-f836dc82.png b/assets/tixing-f836dc82.png new file mode 100644 index 00000000..22a58212 Binary files /dev/null and b/assets/tixing-f836dc82.png differ diff --git a/assets/tsconfig-include-a4dd9125.png b/assets/tsconfig-include-a4dd9125.png new file mode 100644 index 00000000..9c064824 Binary files /dev/null and b/assets/tsconfig-include-a4dd9125.png differ diff --git a/assets/tshirt1-4f8fb94c.png b/assets/tshirt1-4f8fb94c.png new file mode 100644 index 00000000..3a45ccde Binary files /dev/null and b/assets/tshirt1-4f8fb94c.png differ diff --git a/assets/tshirt2-c61c550c.png b/assets/tshirt2-c61c550c.png new file mode 100644 index 00000000..0ba366a3 Binary files /dev/null and b/assets/tshirt2-c61c550c.png differ diff --git a/assets/typescript-as-const-fb61f8e3.js b/assets/typescript-as-const-fb61f8e3.js new file mode 100644 index 00000000..73ca116e --- /dev/null +++ b/assets/typescript-as-const-fb61f8e3.js @@ -0,0 +1,113 @@ +import{d as e,v as c,o as t,a as r,h as s,j as l,k as a,A as o}from"./app-033a95d9.js";const y="/assets/intellisense-e7791f21.png",A="/assets/rename-symbol-ac29a4c0.png",i={class:"markdown-body"},B=s("blockquote",null,[s("p",null,[a("這篇文章討論的是 TypeScript 特有的「常數斷言(Const Assertion)」語法,"),s("br"),a(" 和 JavaScript 宣告常數所使用的 "),s("code",{class:""},"const foo = 1"),a(" 是不同東西。")])],-1),D={id:"%E6%83%85%E5%A2%83%E4%B8%80%EF%BC%9A%E5%B8%B8%E6%95%B8%E5%AD%97%E4%B8%B2%E9%99%A3%E5%88%97",tabindex:"-1"},d=a("情境一:常數字串陣列 "),C={class:"header-anchor",href:"#%E6%83%85%E5%A2%83%E4%B8%80%EF%BC%9A%E5%B8%B8%E6%95%B8%E5%AD%97%E4%B8%B2%E9%99%A3%E5%88%97"},h={id:"%E6%83%85%E5%A2%83%E6%8F%8F%E8%BF%B0",tabindex:"-1"},g=a("情境描述 "),f={class:"header-anchor",href:"#%E6%83%85%E5%A2%83%E6%8F%8F%E8%BF%B0"},E=o(`

🎡 TS Playground

想像我正在建立一個切換難易度的功能,有三種難度可選,先將他們宣告成陣列。

const options = ['easy', 'normal', 'hard']
+
const options = ['easy', 'normal', 'hard']
+

接著再建立一個變數 difficulty,用來記錄目前選中的難易度。

type Difficulty = typeof options[number] // string
+
+let difficulty: Difficulty = options[0]
+
type Difficulty = typeof options[number] // string
+
+let difficulty: Difficulty = options[0]
+

但仔細一看型別 Difficulty 居然是 string,這也太隨便了,因為這代表我可以把 difficulty 設成隨便一個字串,TypeScript 也不會阻止我。

difficulty = '安安' // 😑
+
difficulty = '安安' // 😑
+
`,7),u={id:"%E8%A7%A3%E6%B3%95",tabindex:"-1"},k=a("解法 "),F={class:"header-anchor",href:"#%E8%A7%A3%E6%B3%95"},_=o(`

🎡 TS Playground

這時 as const (const assertion)就派上用場了!寫法就像型別斷言那樣。

const options = ['easy', 'normal', 'hard'] as const
+
+// 或是使用尖括弧(tsx 以外的檔案才能用這種寫法)
+const options = <const>['easy', 'normal', 'hard']
+
const options = ['easy', 'normal', 'hard'] as const
+
+// 或是使用尖括弧(tsx 以外的檔案才能用這種寫法)
+const options = <const>['easy', 'normal', 'hard']
+

如此即可得到預期中準確的型別。

type Difficulty = typeof difficulties[number] // "easy" | "normal" | "hard"
+
type Difficulty = typeof difficulties[number] // "easy" | "normal" | "hard"
+

搭配 VSCode 的 IntelliSense 可以得到代碼提示,也不再怕手殘拼錯字。(快速鍵 ⌘ + I 呼出)

map() 等回調函式的參數也會自動推斷出型別。

// 假設有另一個函式參數要求傳入 \`Difficulty\`
+const foo = (difficulty: Difficulty) => { /* ... */ }
+
+// difficulty: "easy" | "normal" | "hard"
+options.map(difficulty => foo(difficulty))
+
// 假設有另一個函式參數要求傳入 \`Difficulty\`
+const foo = (difficulty: Difficulty) => { /* ... */ }
+
+// difficulty: "easy" | "normal" | "hard"
+options.map(difficulty => foo(difficulty))
+

當然你也可以一開始就先把型別定義好,再以型別約束變數。
但我個人通常不太這麼做,因為這樣會重複出現很雷同的代碼,相當於需要費力維護多個真相來源。例如:

type Difficulty = 'easy' | 'normal' | 'hard'
+const options: Difficulty[] = ['easy', 'normal', 'hard']
+
type Difficulty = 'easy' | 'normal' | 'hard'
+const options: Difficulty[] = ['easy', 'normal', 'hard']
+

不過把型別抽出來單獨定義,還是有額外好處的。假設因為需求改變,需要把 'easy' 改名成 'simple',就可以活用 VSCode 的 重新命名符號(Rename Symbol) 功能來快速重構(從右鍵選單或快速鍵 F2 呼出)。

而這便是 as const 無法做到的,因此建議還是根據實際使用場景做權衡。

',14),b={id:"%E6%83%85%E5%A2%83%E4%BA%8C%EF%BC%9A%E6%97%A5%E6%9C%9F%E5%8D%80%E9%96%93%E7%9A%84-tuple",tabindex:"-1"},w=a("情境二:日期區間的 tuple "),m={class:"header-anchor",href:"#%E6%83%85%E5%A2%83%E4%BA%8C%EF%BC%9A%E6%97%A5%E6%9C%9F%E5%8D%80%E9%96%93%E7%9A%84-tuple"},v={id:"%E6%83%85%E5%A2%83%E6%8F%8F%E8%BF%B0-1",tabindex:"-1"},x=a("情境描述 "),q={class:"header-anchor",href:"#%E6%83%85%E5%A2%83%E6%8F%8F%E8%BF%B0-1"},S=o(`

🎡 TS Playground

想像我正在建立一個從開始到結束的時間區間變數 range,一樣宣告成陣列。

const range = [new Date(), new Date()]
+
const range = [new Date(), new Date()]
+

還有一個參數是時間區間的函式。

function foo (range: [Date, Date]) { /* ... */ }
+
function foo (range: [Date, Date]) { /* ... */ }
+

接著試著把 range 放入函式 foo() 中,但卻出現型別不相容的錯誤。

foo(range)
+// Argument of type 'Date[]' is not assignable to parameter of type '[Date, Date]'.
+//   Target requires 2 element(s) but source may have fewer.(2345)
+
foo(range)
+// Argument of type 'Date[]' is not assignable to parameter of type '[Date, Date]'.
+//   Target requires 2 element(s) but source may have fewer.(2345)
+

分析跳出的錯誤得知:
Date[] 不可指派給 [Date, Date],因為函式只接受長度剛好是 2 的陣列,但 range 卻可能是任何長度的陣列。

TypeScript 說的確實沒錯,即使原本 range 是用 const 宣告,但我在中途還是有機會偷改他。例如 push 新的東西進去,或是執行 range.length = 0 來清空陣列,這些都會改變陣列的長度。

`,9),T={id:"%E8%A7%A3%E6%B3%95-1",tabindex:"-1"},M=a("解法 "),Y={class:"header-anchor",href:"#%E8%A7%A3%E6%B3%95-1"},I=o(`

🎡 TS Playground

試著加上 as const

const range = [new Date(), new Date()] as const
+
const range = [new Date(), new Date()] as const
+

檢查 range 的型別,會發現它從原本的 Date[] 變成 readonly [Date, Date] 了。

type MyRange = typeof range // readonly [23, 28]
+
type MyRange = typeof range // readonly [23, 28]
+

此後便無法再對 range 進行任何會改變它的操作了。

range.length = 0 // 🚫
+range.push(new Date()) // 🚫
+range.reverse() // 🚫
+
range.length = 0 // 🚫
+range.push(new Date()) // 🚫
+range.reverse() // 🚫
+

接著即可成功將 range 傳入函式。

const range = [new Date(), new Date()] as const
+
+function foo (range: readonly [Date, Date]) { /* ... */ }
+
+foo(range) // ✅
+
const range = [new Date(), new Date()] as const
+
+function foo (range: readonly [Date, Date]) { /* ... */ }
+
+foo(range) // ✅
+

留意函式的參數型別多加上了 readonly 關鍵字,那是用來告訴 TypeScript,這個函式不會去動到 range,例如執行 range[0] = ... 來重新賦值陣列的某個項目。

而試圖存取超出陣列範圍的項目時,TypeScript 也會提示錯誤。(JavaScript 的情況則是會無聲地得到 undefined

// ❌ Tuple type '[Date, Date]' of length '2' has no element at index '100'.(2493)
+range[100]
+
// ❌ Tuple type '[Date, Date]' of length '2' has no element at index '100'.(2493)
+range[100]
+

甚至可以很明確地得知參數 length 的型別為 2

type Length = typeof range.length // 2
+
type Length = typeof range.length // 2
+
`,14),L={id:"%E7%B8%BD%E7%B5%90",tabindex:"-1"},N=a("總結 "),Q={class:"header-anchor",href:"#%E7%B8%BD%E7%B5%90"},V=s("p",null,[s("code",{class:""},"as const"),a(" 套用在不同型別的變數上會得到不同的效果:")],-1),K={id:"string%E3%80%81number%E3%80%81boolean",tabindex:"-1"},U=a("string、number、boolean "),G={class:"header-anchor",href:"#string%E3%80%81number%E3%80%81boolean"},J=o(`

字面型別(literal type)加上 as const 後,型別就不會被「拓寬」,例如字串 'hello' 不會被推斷成 string,而是會維持原樣。

let a = 'hello' // string
+let b = 'hello' as const // 'hello'
+
+let c = 123 // number
+let d = 123 as const // 123
+
+let e = true // boolean
+let f = true as const // true
+
let a = 'hello' // string
+let b = 'hello' as const // 'hello'
+
+let c = 123 // number
+let d = 123 as const // 123
+
+let e = true // boolean
+let f = true as const // true
+
`,2),R={id:"%E9%99%A3%E5%88%97",tabindex:"-1"},z=a("陣列 "),H={class:"header-anchor",href:"#%E9%99%A3%E5%88%97"},O=o(`

陣列會被轉換成 readonly 的 tuple,也就是:

  1. 唯獨,陣列裡面的值始終相同,也不能被修改
  2. 長度永遠固定,不能執行 push()pop() 等操作
let a = [123, 'hello'] // (string | number)[]
+let b = [123, 'hello'] as const // readonly [123, 'hello']
+
let a = [123, 'hello'] // (string | number)[]
+let b = [123, 'hello'] as const // readonly [123, 'hello']
+
`,3),P={id:"%E7%89%A9%E4%BB%B6",tabindex:"-1"},Z=a("物件 "),X={class:"header-anchor",href:"#%E7%89%A9%E4%BB%B6"},j=o(`

物件裡的所有屬性都會被加上 readonly,並且裡面的 string、number、boolean 和陣列值都會比照上述處理,型別不會被拓寬。

let a = { text: 'hello', nested: { count: 123 } }
+// {
+//   text: string
+//   nested: { count: number }
+// }
+
+let b = { text: 'hello', nested: { count: 123 } } as const
+// {
+//   readonly text: "hello"
+//   readonly nested: { readonly count: 123; }
+// }
+
let a = { text: 'hello', nested: { count: 123 } }
+// {
+//   text: string
+//   nested: { count: number }
+// }
+
+let b = { text: 'hello', nested: { count: 123 } } as const
+// {
+//   readonly text: "hello"
+//   readonly nested: { readonly count: 123; }
+// }
+
`,2),W={id:"%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99",tabindex:"-1"},$=a("參考資料 "),ss={class:"header-anchor",href:"#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99"},as=s("p",null,[s("a",{href:"https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions"},"https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions")],-1),es={title:"讓 TypeScript 的 as const 救你一命",date:"2021/12/03",tags:["TypeScript"],original:"https://hackmd.io/@xq/as-const"},cs="",ts=e({__name:"typescript-as-const",setup(ns,{expose:p}){return p({frontmatter:{title:"讓 TypeScript 的 as const 救你一命",date:"2021/12/03",tags:["TypeScript"],original:"https://hackmd.io/@xq/as-const"},excerpt:void 0}),(ls,os)=>{const n=c("icon-link");return t(),r("div",i,[B,s("h2",D,[d,s("a",C,[l(n)])]),s("h3",h,[g,s("a",f,[l(n)])]),E,s("h3",u,[k,s("a",F,[l(n)])]),_,s("h2",b,[w,s("a",m,[l(n)])]),s("h3",v,[x,s("a",q,[l(n)])]),S,s("h3",T,[M,s("a",Y,[l(n)])]),I,s("h3",L,[N,s("a",Q,[l(n)])]),V,s("h4",K,[U,s("a",G,[l(n)])]),J,s("h4",R,[z,s("a",H,[l(n)])]),O,s("h4",P,[Z,s("a",X,[l(n)])]),j,s("h2",W,[$,s("a",ss,[l(n)])]),as])}}});export{ts as default,cs as excerpt,es as frontmatter}; diff --git a/assets/typing-typing-876fd270.js b/assets/typing-typing-876fd270.js new file mode 100644 index 00000000..25f3ecba --- /dev/null +++ b/assets/typing-typing-876fd270.js @@ -0,0 +1 @@ +import{d as g,v as e,o as E,a as B,h as A,j as o,k as s,A as a}from"./app-033a95d9.js";const n="/assets/menu-d3272083.png",h="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAAeCAMAAAAlxWNSAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAACZUExURf////+Ttv75/DG2WbSK1SKxTACi6DE6t88QGv9/J7iR1xCo6v7y89IdJ/Xz+/+HM/+Zuj1Gu/P89fH7/v/48+no9/vj5NnF6v/w5fvH1OX36f+/kz+57cWm3/7cypid2/+hv+eGi/uxxZDYpdtKUmNqyf+fXUtTwYDR9Mbs0f+PQuH1/dUtNlfDdrzn+tXx797b8njO8+mOkywZf48AAASrSURBVFjD5ZnZdtowEIZlGxyDZRsvOIQEQmjabGRp3//hqtFotxzknly0p/+FYYxZvmis+WdC0jR/IT49pumNGf9Y2NoP3/I6n3+Tz5tknmyHl9xn2R0hV1l2T0hfZEVvvFYuZ8uSkN1sdknI5Wy2c07pCx8uLNVr+0voKlpR+1R8iCL8rv8OOM/z97Z1f1fbto95roG7rhsAd531lqZpALgR2jLg96axfwilAEzpkQFTumfAe0pjE3gdx0AXxwAcx2t9SuOWaxf4oyxjzRZz4NgSBWD2CGgvN3n+5gCzU4+t/jt0z1Xl8C6qqjIXuXlNkjlTIoXPn0zeU1FkWVYU8gGPVwbwbLlkBziKB3W81Mtb1xeO6rr+0Mu7WkVRtHKEp/h3tXmaDoDT9NEIu+eFTxYwxx3IBs58soBHdTmWz0LqdRp9IgSGFW5twQob+bwZAe66WObzdgS4kWlNae8HPsqsDgEe5rMElll9HpggoKU347beePJZZfU1v2SLKexTkuA+1mM+e8Sy+lco8NqTzyqrb0OBMYUtmSm+GcHlksDzcQngEVyuu2DgEVyu2ykr7AKbK1zJ5bTW1gVOnO1KPZ8KjJuUkAo0cM1lrS3GGhg3KUt4St3DqSvjHhbA7Ia9Vry9LFMKGGoQ6onxbmXwOh34g9ckwVvG4qwChhrE9F3yPqxlrIAPlJchi5eVqaMEHuKCBsCEaOCNNCIaWLkMAG4GXiQceM1dhwCOSewCo8vQwKWMNXDMjYYNTAgCszrsB/4p6zAAY9G9xkyuqmcA5s8nARdKmRFYwCyFAVhW4B376TujDk8ANrN6tToo4Dz380JW30hgSGHwMF23XyyeNzxA8zUFGGwVCp2W0L0BDLaKmyXhtESwmwwM7zpKXpbi8EEInH4iDbyRAADcGfZ6ErAyzuilLXutvTQKvbTQHwAjoABWMQJj6TWXVsSTgM9vWgBMucsQwCz4A+CATUsAYlazfLaBoXlg+ql4VTwFOKAsAfA9dxmiWzoVp34ycEBZEoAsq6+i6Ig3hgn8IttBAfxit4eBwOeNR9F728OpwOeNx0G1TgCsPmUK8PlNKwR4720P3U0LJTYt1FRg1RuKFUZNAA4oSyHWsvC2h25ZEpoZwVRraTaFVhAMfN54/DNeOmjTCrCWfw+wx0grOx1alkKah78FGEc8PuGIJ8h4/EvtIQ7xvMBiavklwGMDgEQNAEKAlyPI7AUL2K3DLvDB2x5+IXDAiCcAmCXjyCJ/SPOAwGC2TKclpj635uDS0x6SLwQ+P8QLASayF3Sl5s4CeG176dIFJt72UAGLHQsfzKNVh3GrqhZG4AInalKrjhr4NFaE1UwLfufOO6a1gOv6AYAxkVlQysmtDayKsGoPJTCUoRve/75BEeb98ZvjpbEMXUunBVPMveG0BDDL32/z+WvTvPNmArL8yZjCw9TySOkdt1nMbJ16ecqcovsG8cQAhnyGC8vy9uLie8kDs3nQHwTOKo571R4S418tOIcG4FbOqV0vjXTopWFOvTe8tAQmBIBlu9gkw7k0K4R33EgzO32i8pQlz79aLGC13ACsXnGBlXfuTW/9HwL/Bpla3N+gKOCsAAAAAElFTkSuQmCC",r="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAAoCAMAAAACNM4XAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABsUExURf///8bd4Nnhx/jrx+KrG/f49OPk2WKEHI2MYx93gXWTOOzx6zqHkPH3+JuadeW1Nv768eOwKJSTbPz14YihUrLDj5C8wSt+h/79+2uLKVaZoMjXwMbFtqmni+nAU/DVi+rq5UuSmnR0dCAgIGjvxwIAAAFTSURBVFjD7ZbbcoMgEEC9RNRs1CBp0aiNSf7/HyssKgk6bWcamXY4TywMHtEF1vMcDofjD1IRk0objwIDos8nvkGmPz40wcdHefJMCdrMIDY46eJzauBrw4zunimONsUA/YKYAKjPAdAsiAHUqw2NqyluAPBrV4yFppgyxryyLBOToRf/41td16Y4rut9JIazK+fpApy/y+khpaZ3MFPqJWsocbyCEh/SFZR4t4YQ5xK1cAwexHuJ8o3BLD4g6MM218WFBBdOMUBx2UeCVno72YZcE5+wC82NDBpd7GcCH70gg7MmLthR8CHFF9kW+eZNKazEU6LPYuxCcTAmuiaWXUqcjYk+i3HvKPGU6E68qdhWcv3OduI/306WDpCXHJn8G0emjUuiEJfEl9di9d/u481Kn6DL88fSZ6PKru9ut8hGTUmS+x1siDPStpWr6R2OF/AJdINBQ5ZvaVwAAAAASUVORK5CYII=",c="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfIAAABQBAMAAAAQHOqyAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAeUExURQ82iP///y1PlktopfDz+MPN4uHm8YicxB1Cj2yDtmgLuo4AAATFSURBVHja7ZrPTxNREMc3JaZ43FjacGsWBLyhRdRjYdF4K2iRa4EqR0ENeiM1QL1J1OCf60zf95V5w9tW9GBSZy779s379cnu5s3O+yZpmlaTJJlKQzuguvuqrkZ1t0b4va0lzkb551F+S+VN4buDvvNhl0rdVU/F+pNNd9ztDJXLcn1i/OqJ6w+MxMiN3MiN3MgnkzzLsrnXoW1R3UWMHP6dArIq+Xax+lUqNyP+PRp7BfdHVN6mOu+fpXumPFPr4Tpy3ZPkPNZXkGOMIfkuNeb5/fhkXWC+oetdR57meb7RD22B6poR8gT+kwLyO/ANlkPlw4hfjl2h8uN+v9cR97voK42qSvvkk+Q7Yi4q9pqCnMdh/3s3Pltv32Geu+mT9Oo1kTYfoaqpNlFy1eYw4j9TdctXb2vwtSgrNdXX0rruH5Kn8B+68SN+IzdyIzfyCSdvt9uv1DSnVLd5Q/Iq9Xmh2vygOrk7zjYajYdi7C6Vv1FdU5F/boR2IsifY4x3dEWEk3AbxqDLIz/OHt18wPjX/T6Gi9n9G5LXCsbZvEEMp/0ihis1IzGmiOF8jBZYJIZLjdzIjdzIjXxiyY+Pjz/xNk/Xd1jpFyr/pOsiXf2qqsfOjtDmHPfSz/1v0/Uj2nB/jiIu6epXXYPf99d+Sf4GbToF5H58JqcxPhWRX7phlN8/lynxzM4wsozBfPAjcyYt5U+Qs5E5GZUzuRbjJcofi9E2C8gjFiWP+43cyI3cyI18csmn6cf1gXdTeVmQV+j+CX5uH6Oe/59/4B+5I8hLqk1dkNX457jtbEf8v7PxFDrzsAFfs4D8YTs0vZ/vif93gfkoJC+yQ3EGE4vhfIw0M3oYR67OaMQ7FZDdJIaTiRFN3oqvpWzkRm7kRm7kE0vOGyGqbmNf3MI975cd5OPbBeRd5NMr6BvLt6P7oH8JuW8fL8xF8unj8u2DyAmxhDQ/D9s69nKe3//D41iBl7Aa5mTKYzQPf3vGUhv9XgTko/wFzzQa40XOWJJQLWLkRm7kRj6B5HmeP4X4SJKzNmkFWqQU+qlnQrPEmqTADz3Sulrue9GmpnRTmFZqm4L5he6pL8l4nhzaJ9l/H/WSnOf35KyNEuQQiXUV+QI0ccPMA5WXlB6uJPwDMmjZpPWozufkK6yF89kPjKP0bMH8/pluOz3ckHw9c7Ya9u+ielGS8/x+LUDw5EyQhmKDtTE5l0gM9ttnLFrj+PYPcjIFOZ/qifhoD0Z/WUZu5EZu5P8Hud8OsGusRMinnTw69SrquiSHfLqO3S5DuSdU3BUovOuKPFNK73XsWFsYh30PaPOV5CWo0I/QpuPE6plUedevdrMsu9rdgOHJU6W8bkbIy6pN4Id8miOZM0QULUQSXsXNkcwpVNyS/CUFU0sikhkovRFJ5ei/LyKiwX6PtVRE9JK7SKUs15e48dme+ncCKm9PHrFW5IxlnF9qHIUGMcjJRDQTI3MuywVfQ8SuaSCTUDOhslFGbuRGbuQTSs7HtwXkfArryapSPj3CzyruFeX3Ku6aS/cOVNrf0Qan0JVv4hTZZ40vUNYqcNlfC821yruBU24chjdClff/aUZu5EZu5EZu5EZu5EZu5EZu5EZu5Eb+7+0XCYyy6Wely/cAAAAASUVORK5CYII=",D="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAC9BAMAAABsT/rOAAAAMFBMVEUUFBQMMDjIyMj/////yBACoujHmQLgGCAkJCQAAATp/9cDZra2ZgB0GABsu/8AAGY9XTa6AAAIfUlEQVR42u1dv28cRRSeGc6Ry8nhRJSz09IEXQRS5OIsRUipMEJ0FEFCtKRzSlsUcUt5ihSK1K78N1BEqVIgQEhp8l+44v2aHxv7cua03p2BWcf23L3d49vvvnnz3pe5oBQc1qp0aJ09KDPEP/ITrLJ5vMgQ/jC+msNFousCLUSHUdJL740oKWRcINqlE2z2U54sKmRcIjqcoPPTwkVFhQgtEx1OsPmd6vy9KiU0d/Q8g6YTdC8V2lxTpYQErfzSeRxHupfNSwn1QcMJurfq2DydFxN6D7TO48rq3sJZTOg90Op2vtrr/NKCQv8F0FXKo8aJWFPKq3JxqXIZr7JgioTXUZoGMUvdVEcToEwXsete12jziVBYSLqAvNuioO41voWFpN8CwiuyEIBiy4RXZNYAaJ0I73shvfReUigu4zUyXaema8weNebpGlfEKmuPZos1W6zZYs0Wa7ZYs8WaLTZYyts55NDOIYc+Wqw97hVjizFojb/tNUAXYosRaos/9YcxI+pSbDGGy9A3gi7GFtv5Fg4RyWXM91cv0oPPirHFEmhlryD6wdu+qouwxQhzoPqaoCe3xXLQ6uNrgp7YFhPMglpk/Mti/7vVxeK3V7++JND3V8/7oCe2xXqgWR2fLs7O958/+Hvx6vzsnED/9BIGPdDT2mI7h6jqQ855QQb75/vni69evl7cv0DQD37HQQ/0tLbYztc6gpZpeLZaIehH7wD0CwL912p1MRjTQ2j6UN0JoGXmvbnE9Nv3J+LEthiATfKg7AFifgSgQROvF2ffiKYHzB4D5Ok7+UTkPO1Wd8/3V6t3i1ewGsLg+eJzEMxQeXqIFbGXPdKKuI8gX19V55Vgi+WgdSqYrgR9rxRbLAdtN4EuxRbbOcSvO/DNT324CSjEFmPQ+DVK59JssWaLNVus2WLNFrNtt1jbLTacLTZaaFBbTI36N7Ztt9jNh9pusbZbTLXdYuUwvU7TRikKGXrk4DAcgpt1RhsdzqKrnPJeufE0ffW0xU//UUhA+857zR9dRNDWyKcvES096YzCR5T5bzx7rMnTRhlNoYxpDDmOwVd8F9Qcr3LGOCNr7GS7xZyzSKo1fLJ38EgzaEf3EN8FTZUA3pVTN/ypzA22GMkDqRbQxLQS3Xqh2uANiErgZDyD72ui3WIgW0Y4p0cdMQ1TkyTgiGMRs8HnnOocnO+Dgm6yyltfTztPEgCcjsVCCkbJOLzG8NvEiibQbg6U0zxxaqrdYm6uGLSbs2IZNOkXlGJCokDkDNoox3nD3XDnsr5HBD2QEgyRagNoFiyApvM6QzOVQHsPtzmxLSYzj5HqKA9QNS4lDBpyoNNB03A65fApbbGgaQJrE2hnO7wjz2ujNjaChqu63ns3vi2GqQzweM5wCBoeS77AlAe/MHbbcyJh3bj8FZst1myxZov9T20xLo8hS2CiMDZb6Gj5dnEZD8Ue1YMalxm8Gq/DOjKdQvWVkRzD2dRQ0neuMxHm7unxJ6enx1vaYsZiGjNY7sNX52nh5jJfGgQpW3X4J06kEqGcPYcrYYHk1K6lbsEFCH5RydUxaE1VDi1K3JUA6F0GvYUt5rFVIaap6KRShPsquBTrjaxBoD9CuEcI1OPgXc4psVvO74jdYJtGxazcQrb24s3t/vzF02dHR0+Pt7PFeJVjovGhSU0A4vc+6oKYDn2MwzLFhRf0gVSj0uoqpQGupKg8YtpKVyKg/zzeyhYjxlAeVN6DHCzXcFEeLjW9WIxYIhC1QGW34qVTMacC2vD9IVZ4MSxlceElphECAgLQR0fE9Fa2GE0SfH+5I/RcLFOlhD2sirOMQl3ozF1klTtGZVgVhotF/INqRyJCoUWaFj52nyFmYHorW8wQTtQD3ha3UzaABpAE07FYlPU2gIa5QH2DMA0gCY6h4qRjn4HIxZk+Z00rmpE6A/30eCtbDDSsMAQsUqZyiEYKamdCgyA4facypq2LTIcKS3kTmmVk2uBDozumgAtIa3OmT7ayxZhpztcivDCFqBVLDQL3XxlozNWRaZ65JmpasTyYiBy0wCDUPaL/hS1msB61ZMiQ/ih7KKmq4ZjHW0CJ9Zm2LqJVuuM2mVcWnLOscENM86sJ0wn0k5PtbDGUckcLHLHCRkeAJZrm/xret7OhyUGpJNtJNE3gPHaY/IL4Eqhpm0DHYghQI9Fb2WLsZ+DKhjLGFALT0oUEjg2ClQYBsgomluCOUXJPuqCcI9079sg8pTkjcX/G/YVNoP94clLdhyhhId/aFpvsb4N2T0+2tcXahyjHssXahyjbhyhV2y3Wdos1W6zZYs0Wq9YWa7vFBi2Y2m6xEUJtt1jbLababrHpQxtssbKZbv+22FjZo8Y8XeOKOETtsfcQjiWEZjRQ2SBeFZ8pxRZ7SAeEeKDSIF0VQ4XYYkD0coZUz4BKHMCPpaaBFYIppGc51RPbYjMBTaD2BDQNdCCYQlZAF2GLAZbZknHKAJ85mDH3M6J6LwxKscVmGda9CHq59/BARdAYiqBLsMXoXWesB1HTcRBAS6gUWywDvVwPepmBLsAWy+URIa4blGKLZaAP1oM+yEAXYIv1JmKOtT8RRfyF2GK9lNcjuJfydARdgi12i1eQx3Gg4jNhjqZQKbbY3R8Qz5eP7S0ZqDjYE9ApVIotdutHOr4Pg8fZgAnOQqXYYldgjYPIfQJdiC3GgK45aLZYs8WaLdZssWaLNVus2WIl2WIjhoazxUYMDVZPjxkazBYbJDRbcj7DRIyQLn8Pa4sNEkJHFT8pgRvt6XN4l74L3C0moAnc1aAL3C0m8iAZZJLIpdJ2i00Iuu0Wu7FQWSlvbFtszFC9y3ilu8V6XUDppan0AFX+7+iRal/NEfm1dYHWhfoEm0JFOjIbQmV6XxtC/wA7fAjScgGBOwAAAABJRU5ErkJggg==",p="data:image/png;base64,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",m="data:image/png;base64,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",b="data:image/png;base64,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",l="data:image/png;base64,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",d="data:image/png;base64,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",W="data:image/png;base64,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",L="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAACgCAMAAADD9vgfAAAAclBMVEUUFBT/yA7///8AZsxILBcfsk+ZmZkMMDjIyMgAAAAVfTgAWoJ0AADurgDDAAAyMjKqAAAfIidM/00AVar/mmOjeACQ/5UATJiE/QDX19cLl1fV/3Ra1QD/dCtZpQCYwpOzhwD59vNDAwMCTJaR9v8AQ5mnNnahAAAN8klEQVR42u2djWLbqBKFpaS+SE7bTXxvvIntpG22+/6veD1CWIBA4meQHPewu4nNV7Ms6BgGZmar6lKE9tMqQEBA3RvnnwUCAureev4sEBCQX0tAQED4lgACmkTYZwIB+RFOKoCA/Ej492RAQH8qclQAsaEHKhZqGuMzTWOgRv7T9O8vBcO7FnJUCCAe9PCgFKKLoLkIYHiv3qi/LgLpREM/MLxrIUeFAOJAD6pY64cw9GGsKUJfQYS2gmB410OOCgGUj0ga50f7ssuyNk1jfXTi6NaR4c9qGMO7FnLYKAIoF3X66P95GLZQjTQ75GM/0otmgwhlk1wohnclJLyHXEDpSAnkwbOE0BuhlgilF7l+qB1VY2/AMLxrIPc9iQDKQz6BaIoQ5tteD+rUqmnsBQbDuwby3bQLoCw0LxBhLShVb4EIeaqr9mPWGoLhXRb5fbUEUA5y2SC6IoSoDAP9YoMMgrFsEAzvGgiDUAq5TrEuivAJ5GKDDDZJg+FdFWEZLYUeRhssUxDCFki/WohqfJOO4V0PwRArhbz66B95YUpAs9H7l5W1gmB4V0E4yiuFOn1gND49wmUQENAEgjsBENDklgq+akBAoQqBtzMQkF8hiJfJRs0qBSNflQuYQsQlJ1pXIJgURjSqQsx+NrKCahfrhr2GYFKYEVK78CC3QMp3w1pDMCnsCMnBGJBHHot0Q19DMCkFENJL5iO/QMp3w2GHYFIYEb4lsIIA+RH2mRzIkcNqsW4YCR4wKcwIJxUsaJzDarluGKG5mBRehGNwHjTKYbVgN6z0J5iUfOSoAMpCfaTHKt3oE85hUhiRowKuODnIfkiX7MZInJgUpB69NmRneVuyG+PtHSYlH1Xw+edEpqG8bDccBwSYlHyEqDFONM6lu1w3XEfMmJRshLhjTjRyO1+wG85LSkxKJkLmClZku3ss2Y0JP2LMVypC7iNeZKXpWbQbfj9izFcqQvY8ZmQ8pAt3w+cHhvlKRxgECARoCmEZxRYLaArBEIORDjSFcJTHiXDMi9SjQBMIF4W3h+BOAFcToMktFXzV2BCcFW8Njarg7Qx3dyC/QhAvg4ApIL9CgPIQQm5vCo2qELOfiZC04WYRUrtwIKT9uV2E5GAMCInjbhghvWQ+QurR20X4lsAKAuRH2GfCBgHyI5xU4BQLyI9wDM6DcA9yU8hRAYSbdKCpCrjiwBcLaKoCzpwZCN68t4Yq+PxXiAcBitAHAsoyECIKbw4h7pgTISYdqUeBJhCymiD1KNAEQl4spB4FmkDIrHhbCIMAgQBNISyj2GIBTSEYYldhpLdmWcZIf30do64uazRa539LWoNWS23OfH05l/9Y5YtWkHr0io957YdqmWNeKQbhEEjOaDgFkthg6/3iiG9wTiDeBnEZVK18UdhWX7vy/VKqKqEbkReFr+diP4BUlzcabftkFXqs0xps281mdy77/fH44we9vkgkocEwgSD1aFmU5GrStrZAxmsIv6uJXyA5o+EUSGKDZ4EcBoEcDoNAUhoMFAhSjxZFCc6KtH48P//8+diVb+fSSaSK7kaksyLJ4++u3N3d94UkkjMaJI+/RqVtUxps293ux4/9fnsgcez39G6z6dpK6qESwpxAkHq0JEpwd6f14yyQZ1sgpd3dfQLJGY0AgQQ3qARy2AwCOWyMDVtUg8ECQerRgighYKptn59JFoNASCJt8YApKY/7+1+/7u50ieSMRohAQhts2+ORBEGfPh7p9/HYbbIS5ytcIAirLIcSQm69AikcchsqkJhuBAkksEGfQFLnK0IgSD1aDCUkbSCB0PRLachXvQ1SNGlD2/7ztxTFIA+nQCK6QQLZaEXKZdRmUIPSRKfxoG0W/d5oZnp8g7ooXK/nGkRqFw6UkPZnQiBF0/6ECyS8G4ECCWpQF8hmRiAhDUYJBKlHuVBd1zpKSBxHApGbrKFcTrGSE8eZ/XJ9SgpEiUP99n1Dh3XDFoiUiHPbNtsgGenbLRnnShwvL/u9RyABDcYJBKlHmVBtPokJqUcnBZKaerT2CUTfDoULJLQbwQIJaDBOIPMNRgoEqUc5UF2bT2LaCkLHvKY8slcQu2POFUQa6WbhWEHoBpwK/Zbv01aQ4/HlRVofUiJ0YZi+glAbdByhG+v0m+oc/m9IPcqBrOcw0QbxCyTZBvEJxLBBggUSaYPMCyTMBokQSECDcQJB6lEGNFo/Ek+xSCBD0QSSc4rllIh5ivX29j+jcJxibbebUUk9xdrtjkfaVO12JBN5XZh+ihUlEKQeZUAOfaTdg/gEkncP4lCIdQ8SKJCoe5AQgYTegwwC2R4GgaTOV4xAcPHNgUb7qyrxJl3fZD0/S2dF48Y46SZ9LBD7Jv3tjSQiNzBKIrk36SSQwfnRKZDgm3Rpmu/3hwNdEZKRbo1LVINfZkqYPuBVFYGMRzDDF8stkExfrCl99P9et0DyfLHmBRLuiyXd3ekWxBBIpi/WvECQepQNDU9hXupRR2BQXupR2bHJT9G/5+1NikSV8Td0rDfvdjveZKV584rKF52Y5807KxCkHuVDF4Fkph4dPwiZ8SB9x6ajLTwCyRioeYHENeiLKEyNBwkRCFKPsiIpkMyIwj5gaigTpnJYRKG1sPk+NfcNHTsa1Mb2UpQ8tG1bZEThbnc6yQtC+nl6yY0oVEa5XuYiChFdnoVMfaTFpAcJJDImXdkf05+a+YaOHg2fQNIanBBIYkz6vECQevTqspq07c+fJJHHx9+/Hx/Pv88mel371hDOrCZP3WXh/f3rq3Q0sS2g+NEgedii2wxBTlENyk++vx+2+/1ZHKf3dxKMuYbEZjWZEghSjy6BEvJiOQXyrY3vRnReLHp8PQJJHA2/QOIbvAjk0AvkZAgkusHZFQSpRxdACZkVlUD0DdZ5BYnvRmRmRRKHFIgsVUUipSIf6PjRcF8TKjM9tsG2pWPd3e79nTZaMibkdNJW1sgGlTjote6gqESC1KMQiEMg//47eGFdm0BOJ10g242UzKICwWbpGrZYdDGoHk7aZMma0lssStDQtr9+fXzc3T1+JXcLudGiVymjoeQhzXP1WtW6jq7ntli0oXp5kfKQJvrmkLfFmhQIUo9eq5EuBaJyYnkFwmyk6wL5agskYTTmBRJrpDsEsskz0qcEgtSjV5t6tHWU3GPekEPUjw+fQFJGY3AzURIZ3qsHO/aYl4x0yoxFZ1gqgUPOMS+JQQ+OotfDFitYIbgNTEaJF4UBAmFPPTolkJTRCBFI7EWhVyCJF4XzAkHq0bIo2dXk+/dv32ib1YXb5ruaBH3KFqVhpEePhhTCsKlS5rm2xariXU26C8LT4eC8JoxqMFAgSD1aFCU7K/YCeVQCWeL/k06ieKqexJP8W3z9GAkkosEwgcT2UN6gbzZuR5OYBkMFgtSjJVGCu7sUCK0e8ltcGeilU4+qo1493NaxvQtuUDmZtO1/tTLUpgxv2x4OdINOqX/o9/KzPKpCVNTSqUd9AimdetQlkKp1R2eHNBgqkJge0rkV3YZ0Ajn57JiCszyqAspDCSG3g5luviqdelQJk7ZUbau2VjmjoVYgUyDjVSm8h65xWXKWR1XIzJCJEpI2CK9AyqYedQokazRCBRLeQ8e4rDbLyO3DgRLS/rChyNSjrgeQoxtSIEoo/iwkn26WkR2OASUkjmNDRurRsE95Vq6sboQI5HPOMvKL5qOE1KNsyHZzWakbYaL7hLOMteDPW0FKoCCBfMJZhjXxB9ogQMEI51FrnmKxoIRTLKBQhBsNHpR4D8KCEu5BgKqwexDciTOhxJt0FpRwkw40gxwV8KpawReLBSX4YgHNIUcF/HIzUKI3LwuK9uYFCkAVIjuq9eNBWFB0PAhQCEJsICdKiihkQpERhUBhCNHlnCgpJp0JRcakAwUh5CdhRQlZTdhQdOpRoHmEDFe8KCEvFhuKTj0KNIuQI5EZmZkVG+cz2502Ca9jY3I3IjMrAgUgDEJRgQj3I0vHTQWeZwikAMIyWnSL5RNIkR0RtlglEAyxkkZ6IxwLS/+u+zl6qGGkXxvCUR4nso55G/qLfnS/FVQVlQ1wzHuNCJdBVbGLQqWBQRWd7d4LQ1h/BBeFV4ngTlDO1aTpdlmmQIRaP5ouwEoXCFxNrhBVcEgr56zYPfrCEIg03QddaAKBs+IVolEVvJ0Z3d17TWg6ELJWvReaQODufq3u7oiXYUPmQ9o00gDvTmCbRm6o1Laq0SobV6guAqbWR6MqoDyEkNubQqMqxOxnIiRtuFmE1C4cCGl/bhchBRwDsi7r6vPfspw/U6uKAbJ240oSx90uQhLRfGQLpO5lcP4p6E1XUVsCua3UozeL8C3BvoLUSiK0gtSiljUVVhCkHoUNohTQi0TINaTfbxkCgQ3yGRBOKgqcYtVyVyU1cf4htA3WRSA4xfoMCMfgPMi6B1GbKnEx0Ec2CO5Brhg5KoD4btLV4lH39rmSi7Lba95u4CYdqUevHlkPqbLSa30FqbXVBalHrxw5KuDMmYGMbU4nBml5KJXU3Wmv2mDVSD169aiCz39VLB6k1i4K+1XEsEBEzdkNxIMUQYga40RWRKF+USiXE7miVMr+qBm7gYjCIghxx5zIiC6v636R0EyPWgml/1Rds3UDMeklEDJXsCKkHkXqUaAJhNSjSD0KNIn0NWTJbkz8D6gxKckIgwCBAE0hLKP8qFmlYOTLIBhi7GglgWDkyyAc5QEBIfUoEBBSjwIBIfUoENCCaFQFb2cgoL78H72Y0no4I5ZiAAAAAElFTkSuQmCC",F="/assets/char2-9248d6c0.png",Q="/assets/char3-5ba16f0d.png",x="/assets/game1-d689a75e.png",R="/assets/game2-ec9b0a6e.png",C="/assets/game3-ae563b09.png",Y="/assets/game4-ad0db896.png",k="data:image/png;base64,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",y="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQIAAACPBAMAAADjOMjRAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAPUExURRQUFP/ID8jIyCAgIAwwOJtnW3wAAAOzSURBVHja7ZxtkqQgDIaDzQHIDSxOYFXPAfix9z/TJgQEZ6Z6xQ8yPYvVIgJVPPMmINFRAAALsKYAoUr7VK3HUFfajlWfjhusnlU5k5tIpk/V2iTm1iac61RVmlC2NOFsn6qqCeWrJhA6VdVN4E99YjtVSalX2WoraBJEETyobJUIugQkghJAJYIewZOShURQJHguQD+rSfAEptAmeOoTLOoEz0EwCAbBIBgE+wgMogNAxHzu+Bf3XGvQpZpYkmtTk5cED3UCOEDABVQkxbkrxkLZEoypzl4RPN6SYDVEgkk0jBFRvprq5Ur1HQmM9Jh7LfbIMMk1zQ4jvCtBkltUzu5oihX2DsR3JpDORGLJuM1ozEw7R+NbEmyskDsvs7LJM1I1Uf9CApQx6JLYrrpKlVn51tF4JUE45IlF2mgF3AxB89kgrwmsOgGEY6MxjzSzjkn3xQrZP/9BYNUJQHulukeDmwmCNoFVt0I4QEB+Bw4cuRkC5Wgg7vC4Sz2RendoHPXqZAMHnQnob6aNBicC54zD7gTx73aiRsp2JTDSLTqxh2Ml+hJcPB88BgGo38F4jLs4g2AQDIJBMAgGwSC4mMAZY3jNTEkMYzhgBryJYL1ZV9+kc0bWzVxAO0aWltV7CwFuN6HiDfgnOHKzAe8hMLFTTmR3iWDVgMNHShujyLMEIHfZUwxDARy7wV1WYAIs+6oBm0Bu67MURMOhZEcCjFYQgUQDIsIbrRA9sCKIKshO5RxEkwbgOhKIJ1KK3HflmzdagdMNQcxhvLEiD3nYMfsRdJ2V02jMlvgpBNM8efDTNME0+bsJvp2VY9+czn66/9r43ZWJNJijBgyhcnVmDThhHUCLwLMv9PGDX7xKWwbBDyCAQfC02gRL0Cb4OKVBfrTMyyNaocQnP2ja4oXQ7gfVE20JVPIeCWiJBE0E0E5Qx0w5ZohIMW5sXD8dIWCRTV4ncviSNDASPZoYvO0nWM4SOFOsACl0wPsJ8pPuTBCtImvlThpUBMUKBzWAs1bYjAUpbVpFX0kgsZtpHQtw0grrP8Nhipk6abASXDArn7TCdQTLQQIKEmK8ALRSpniBzvw8Ty0r90wAB2flaY0VZk5o5S4BTDNB27s8ZhMzcawwJ4J5nkkL307wcfi1tk8akBXmIwTL8ffaJu7Yp86LH/hGAt332tIxqBNYdQL4nwmCOoHVt0JQJ7BW3RPD0EB5MPjyBQKvLUHQevO++giDEkGRoDqMr5GMr5GMr5GAztdI/gI6jFRw/0RgDAAAAABJRU5ErkJggg==",T="data:image/png;base64,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",M="/assets/interface3-2a7d9ac4.png",U="/assets/interface4-6491aa95.png",N="/assets/interface5-fd8930fa.png",K={class:"markdown-body"},V=A("p",null,"物件導向程式設計期末專題。",-1),J=A("p",null,[s("這是以 "),A("a",{href:"http://zty.pe/"},"ZType"),s(" 作為靈感來源而開發的打字射擊遊戲,以 Visual Studio 2015 建立專案,基於 "),A("a",{href:"http://www.cc.ntut.edu.tw/~wkchen/game/"},"Game Framework 4.8"),s(" 以 C++ 語言撰寫的 Windows 平台遊戲。")],-1),S=A("p",null,[A("img",{src:n,alt:"主選單"})],-1),j={id:"%E8%A9%A6%E7%8E%A9%E5%BD%B1%E7%89%87",tabindex:"-1"},Z=s("試玩影片 "),u={class:"header-anchor",href:"#%E8%A9%A6%E7%8E%A9%E5%BD%B1%E7%89%87"},I=A("div",{class:"embed-responsive"},[A("iframe",{class:"embed-responsive-item",src:"https://www.youtube.com/embed/j8L_ViHDzMY",allowfullscreen:""})],-1),O={id:"%E7%B0%A1%E4%BB%8B",tabindex:"-1"},f=s("簡介 "),P={class:"header-anchor",href:"#%E7%B0%A1%E4%BB%8B"},G={id:"%E7%8E%A9%E6%B3%95",tabindex:"-1"},H=s("玩法 "),X={class:"header-anchor",href:"#%E7%8E%A9%E6%B3%95"},v=A("p",null,[s("在本遊戲中有各種不同外貌的敵人(Enemy),但是他們都有一個共同點,那就是他們的身上都帶著一組英文單字。玩家必須在敵人接近之前,輸入其身上的單字,才能成功的把的敵人消滅。"),A("br"),A("img",{src:h,alt:""}),A("br"),A("img",{src:r,alt:""}),A("br"),A("img",{src:c,alt:""})],-1),w={id:"%E8%A6%8F%E5%89%87",tabindex:"-1"},z=s("規則 "),q={class:"header-anchor",href:"#%E8%A6%8F%E5%89%87"},_=A("p",null,"本遊戲除了普通的敵人外,另外還有多種強大的Boss,除了字數較長外,還各自擁有不同的技能。",-1),$={id:"%E7%89%B9%E6%AE%8A%E5%8A%9F%E8%83%BD",tabindex:"-1"},AA=s("特殊功能 "),sA={class:"header-anchor",href:"#%E7%89%B9%E6%AE%8A%E5%8A%9F%E8%83%BD"},tA=A("p",null,'當身邊有太多敵人即將接近,而且來不及將他們消滅的時候,玩家在每場遊戲,有三次的機會可以使用技能。按下"Enter"釋放出電磁脈衝(EMP),快速的將身邊的敵人消滅。',-1),oA={id:"%E5%9C%96%E5%BD%A2%2F%E7%BE%8E%E8%A1%93",tabindex:"-1"},aA=s("圖形/美術 "),iA={class:"header-anchor",href:"#%E5%9C%96%E5%BD%A2%2F%E7%BE%8E%E8%A1%93"},gA=A("p",null,[s("我們遊戲圖形是以8 BITS復古風作為設計風格,它最大的特色就是在圖形的邊緣,看起來會有明顯的鋸齒。為了達成像素畫(Pixel Art) 的美術風格,大部分的圖形我們都是利用"),A("strong",null,"小畫家"),s("來繪製。和一般的繪圖軟體相比,小畫家非常適合來製作點陣圖,因為他可以很精確的在每一格中填色,並且可以很輕易的輸出Bmp格式的圖片,正好符合我們的需求。")],-1),eA={id:"%E8%AA%AA%E6%98%8E%E7%95%AB%E9%9D%A2",tabindex:"-1"},EA=s("說明畫面 "),BA={class:"header-anchor",href:"#%E8%AA%AA%E6%98%8E%E7%95%AB%E9%9D%A2"},nA=a('







',1),hA={id:"%E8%A7%92%E8%89%B2%E9%81%B8%E6%93%87",tabindex:"-1"},rA=s("角色選擇 "),cA={class:"header-anchor",href:"#%E8%A7%92%E8%89%B2%E9%81%B8%E6%93%87"},DA=A("p",null,[A("img",{src:L,alt:""}),A("br"),A("img",{src:F,alt:""}),A("br"),A("img",{src:Q,alt:""})],-1),pA={id:"%E9%81%8A%E6%88%B2%E7%95%AB%E9%9D%A2",tabindex:"-1"},mA=s("遊戲畫面 "),bA={class:"header-anchor",href:"#%E9%81%8A%E6%88%B2%E7%95%AB%E9%9D%A2"},lA=a('

輸入怪物身上對應的單字攻擊怪物
BOSS怪物會發射小怪攻擊玩家
按下空白鍵可以釋放EMP擊退怪物
通過關卡
Game Over

',1),dA={id:"%E5%85%B6%E4%BB%96%E4%BB%8B%E9%9D%A2",tabindex:"-1"},WA=s("其他介面 "),LA={class:"header-anchor",href:"#%E5%85%B6%E4%BB%96%E4%BB%8B%E9%9D%A2"},FA=a('

暫停視窗
最高紀錄
遊玩記錄
清除遊玩記錄
關於頁面

',1),QA={id:"%E9%81%8A%E6%88%B2%E4%B8%8B%E8%BC%89",tabindex:"-1"},xA=s("遊戲下載 "),RA={class:"header-anchor",href:"#%E9%81%8A%E6%88%B2%E4%B8%8B%E8%BC%89"},CA=A("p",null,[A("a",{href:"/files/TypingTyping.zip"},"TypingTyping.zip (7.7mb, Windows only)")],-1),YA=A("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=Typing-Typing&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),UA={title:"Typing Typing!",briefDescription:"8-bit 復古風格打字遊戲",githubLink:"https://github.com/ngseke/Typing-Typing",period:["2017/02","2017/06"],members:["余鎧企","黃省喬"],cover:"/img/project-cover/typing-typing.png",tags:["C++"]},NA="",KA=g({__name:"typing-typing",setup(kA,{expose:i}){return i({frontmatter:{title:"Typing Typing!",briefDescription:"8-bit 復古風格打字遊戲",githubLink:"https://github.com/ngseke/Typing-Typing",period:["2017/02","2017/06"],members:["余鎧企","黃省喬"],cover:"/img/project-cover/typing-typing.png",tags:["C++"]},excerpt:void 0}),(yA,TA)=>{const t=e("icon-link");return E(),B("div",K,[V,J,S,A("h2",j,[Z,A("a",u,[o(t)])]),I,A("h2",O,[f,A("a",P,[o(t)])]),A("h3",G,[H,A("a",X,[o(t)])]),v,A("h3",w,[z,A("a",q,[o(t)])]),_,A("h3",$,[AA,A("a",sA,[o(t)])]),tA,A("h3",oA,[aA,A("a",iA,[o(t)])]),gA,A("h2",eA,[EA,A("a",BA,[o(t)])]),nA,A("h2",hA,[rA,A("a",cA,[o(t)])]),DA,A("h2",pA,[mA,A("a",bA,[o(t)])]),lA,A("h2",dA,[WA,A("a",LA,[o(t)])]),FA,A("h2",QA,[xA,A("a",RA,[o(t)])]),CA,YA])}}});export{KA as default,NA as excerpt,UA as frontmatter}; diff --git a/assets/typingtyping-b132256f.js b/assets/typingtyping-b132256f.js new file mode 100644 index 00000000..f0f7fe5e --- /dev/null +++ b/assets/typingtyping-b132256f.js @@ -0,0 +1 @@ +import{m as o}from"./app-033a95d9.js";const f={};typeof o=="function"&&o(f);export{f as default}; diff --git a/assets/useOgImage-203f60bd.js b/assets/useOgImage-203f60bd.js new file mode 100644 index 00000000..1de06da0 --- /dev/null +++ b/assets/useOgImage-203f60bd.js @@ -0,0 +1,18 @@ +import{C as le,I as Ne,J as $e,K as Be,L as se,M as ce,N as Pe,d as R,o as x,a as S,h as _,r as Z,O as Y,u as B,_ as G,j as U,w as ue,F as Re,i as je,t as N,b as $,q as H,P as Ae,Q as Me,R as qe,S as Ue,U as Ve,W as oe,X as me,D as Ye,c as We,k as fe,Y as De,H as Ke,Z as Ze,$ as Fe,a0 as Ge,p as Xe,a1 as Je,a2 as Qe}from"./app-033a95d9.js";/*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */var L=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},a=window.Promise||function(r){function d(){}r(d,d)},i=function(r){var d=r.target;if(d===A){E();return}y.indexOf(d)!==-1&&J({target:d})},m=function(){if(!(T||!n.original)){var r=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(Q-r)>s.scrollOffset&&setTimeout(E,150)}},z=function(r){var d=r.key||r.keyCode;(d==="Escape"||d==="Esc"||d===27)&&E()},c=function(){var r=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},d=r;if(r.background&&(A.style.background=r.background),r.container&&r.container instanceof Object&&(d.container=L({},s.container,r.container)),r.template){var u=V(r.template)?r.template:document.querySelector(r.template);d.template=u}return s=L({},s,d),y.forEach(function(g){g.dispatchEvent(I("medium-zoom:update",{detail:{zoom:v}}))}),v},f=function(){var r=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(L({},s,r))},w=function(){for(var r=arguments.length,d=Array(r),u=0;u0?d.reduce(function(l,h){return[].concat(l,re(h))},[]):y;return g.forEach(function(l){l.classList.remove("medium-zoom-image"),l.dispatchEvent(I("medium-zoom:detach",{detail:{zoom:v}}))}),y=y.filter(function(l){return g.indexOf(l)===-1}),v},he=function(r,d){var u=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return y.forEach(function(g){g.addEventListener("medium-zoom:"+r,d,u)}),j.push({type:"medium-zoom:"+r,listener:d,options:u}),v},_e=function(r,d){var u=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return y.forEach(function(g){g.removeEventListener("medium-zoom:"+r,d,u)}),j=j.filter(function(g){return!(g.type==="medium-zoom:"+r&&g.listener.toString()===d.toString())}),v},X=function(){var r=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},d=r.target,u=function(){var l={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},h=void 0,b=void 0;if(s.container)if(s.container instanceof Object)l=L({},l,s.container),h=l.width-l.left-l.right-s.margin*2,b=l.height-l.top-l.bottom-s.margin*2;else{var C=V(s.container)?s.container:document.querySelector(s.container),O=C.getBoundingClientRect(),D=O.width,Ee=O.height,xe=O.left,we=O.top;l=L({},l,{width:D,height:Ee,left:xe,top:we})}h=h||l.width-s.margin*2,b=b||l.height-s.margin*2;var k=n.zoomedHd||n.original,Se=ae(k)?h:k.naturalWidth||h,Oe=ae(k)?b:k.naturalHeight||b,M=k.getBoundingClientRect(),Le=M.top,Te=M.left,ee=M.width,te=M.height,Ce=Math.min(Se,h)/ee,Ie=Math.min(Oe,b)/te,K=Math.min(Ce,Ie),He=(-Te+(h-ee)/2+s.margin+l.left)/K,ke=(-Le+(b-te)/2+s.margin+l.top)/K,ne="scale("+K+") translate3d("+He+"px, "+ke+"px, 0)";n.zoomed.style.transform=ne,n.zoomedHd&&(n.zoomedHd.style.transform=ne)};return new a(function(g){if(d&&y.indexOf(d)===-1){g(v);return}var l=function D(){T=!1,n.zoomed.removeEventListener("transitionend",D),n.original.dispatchEvent(I("medium-zoom:opened",{detail:{zoom:v}})),g(v)};if(n.zoomed){g(v);return}if(d)n.original=d;else if(y.length>0){var h=y;n.original=h[0]}else{g(v);return}if(n.original.dispatchEvent(I("medium-zoom:open",{detail:{zoom:v}})),Q=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,T=!0,n.zoomed=nt(n.original),document.body.appendChild(A),s.template){var b=V(s.template)?s.template:document.querySelector(s.template);n.template=document.createElement("div"),n.template.appendChild(b.content.cloneNode(!0)),document.body.appendChild(n.template)}if(document.body.appendChild(n.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),n.original.classList.add("medium-zoom-image--hidden"),n.zoomed.classList.add("medium-zoom-image--opened"),n.zoomed.addEventListener("click",E),n.zoomed.addEventListener("transitionend",l),n.original.getAttribute("data-zoom-src")){n.zoomedHd=n.zoomed.cloneNode(),n.zoomedHd.removeAttribute("srcset"),n.zoomedHd.removeAttribute("sizes"),n.zoomedHd.src=n.zoomed.getAttribute("data-zoom-src"),n.zoomedHd.onerror=function(){clearInterval(C),console.warn("Unable to reach the zoom image target "+n.zoomedHd.src),n.zoomedHd=null,u()};var C=setInterval(function(){n.zoomedHd.complete&&(clearInterval(C),n.zoomedHd.classList.add("medium-zoom-image--opened"),n.zoomedHd.addEventListener("click",E),document.body.appendChild(n.zoomedHd),u())},10)}else if(n.original.hasAttribute("srcset")){n.zoomedHd=n.zoomed.cloneNode(),n.zoomedHd.removeAttribute("sizes"),n.zoomedHd.removeAttribute("loading");var O=n.zoomedHd.addEventListener("load",function(){n.zoomedHd.removeEventListener("load",O),n.zoomedHd.classList.add("medium-zoom-image--opened"),n.zoomedHd.addEventListener("click",E),document.body.appendChild(n.zoomedHd),u()})}else u()})},E=function(){return new a(function(r){if(T||!n.original){r(v);return}var d=function u(){n.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(n.zoomed),n.zoomedHd&&document.body.removeChild(n.zoomedHd),document.body.removeChild(A),n.zoomed.classList.remove("medium-zoom-image--opened"),n.template&&document.body.removeChild(n.template),T=!1,n.zoomed.removeEventListener("transitionend",u),n.original.dispatchEvent(I("medium-zoom:closed",{detail:{zoom:v}})),n.original=null,n.zoomed=null,n.zoomedHd=null,n.template=null,r(v)};T=!0,document.body.classList.remove("medium-zoom--opened"),n.zoomed.style.transform="",n.zoomedHd&&(n.zoomedHd.style.transform=""),n.template&&(n.template.style.transition="opacity 150ms",n.template.style.opacity=0),n.original.dispatchEvent(I("medium-zoom:close",{detail:{zoom:v}})),n.zoomed.addEventListener("transitionend",d)})},J=function(){var r=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},d=r.target;return n.original?E():X({target:d})},ze=function(){return s},ye=function(){return y},be=function(){return n.original},y=[],j=[],T=!1,Q=0,s=o,n={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?s=t:(t||typeof t=="string")&&w(t),s=L({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},s);var A=tt(s.background);document.addEventListener("click",i),document.addEventListener("keyup",z),document.addEventListener("scroll",m),window.addEventListener("resize",E);var v={open:X,close:E,toggle:J,update:c,clone:f,attach:w,detach:W,on:he,off:_e,getOptions:ze,getImages:ye,getZoomedImage:be};return v};function at(e,t){t===void 0&&(t={});var o=t.insertAt;if(!(!e||typeof document>"u")){var a=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css",o==="top"&&a.firstChild?a.insertBefore(i,a.firstChild):a.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}var rt=".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}";at(rt);const it=ot;function dt(e="*:not(a) > img"){le(()=>{if(!Ne)return;const t=it(e,{margin:16});$e(()=>t.detach()),Be(ce(),async o=>{await se(),t.update({background:o?"#1B1917":"#fff"})},{immediate:!0})}),Pe(` + .medium-zoom-overlay { + z-index: 100; + } + + .medium-zoom-image.medium-zoom-image--opened{ + z-index: 101; + } + `)}function lt(){le(async()=>{var t;await se();const{hash:e}=location;if(e){const o=e.split("#")[1];(t=document.getElementById(o))==null||t.scrollIntoView()}})}const st=R({__name:"Prose",setup(e){dt(),lt();const t=ce();return(o,a)=>(x(),S("div",null,[_("article",{class:Y(["prose",{dark:B(t)}])},[Z(o.$slots,"default",{},void 0,!0)],2)]))}});const ct=G(st,[["__scopeId","data-v-eb8991d3"]]),ut={},mt={class:"container px-4 py-8 md:pt-16"},ft={class:"mx-auto max-w-3xl pt-10 md:pt-16"},gt={class:"mb-12 border-b border-black-300 pb-4 dark:border-black-700"};function vt(e,t){const o=ct;return x(),S("div",mt,[_("div",ft,[_("header",gt,[Z(e.$slots,"header")]),_("main",null,[U(o,null,{default:ue(()=>[Z(e.$slots,"default")]),_:3})])])])}const At=G(ut,[["render",vt]]),pt=R({__name:"ProjectTags",props:{list:{default:void 0},size:{default:"md"}},setup(e){return(t,o)=>e.list?(x(),S("ul",{key:0,class:Y(["mb-2 flex font-medium",{"space-x-1 text-sm":e.size==="md","space-x-2 text-xl":e.size==="lg"}])},[(x(!0),S(Re,null,je(e.list,(a,i)=>(x(),S("li",{key:i,class:Y(["rounded-md bg-black-400/20",{"px-1":e.size==="md","px-2 py-1":e.size==="lg"}])},N(a),3))),128))],2)):$("",!0)}});let ge=(e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce((t,o)=>(o&=63,o<36?t+=o.toString(36):o<62?t+=(o-26).toString(36).toUpperCase():o>62?t+="-":t+="_",t),"");var ie={created(e,t){const[o,a]=t.value;e.setAttribute(o,a||ge(5))},getSSRProps(e){const[t,o]=e.value;return{[t]:o}}},F="__wrap_b",P="__wrap_n",de="__wrap_o",ve=(e,t,o)=>{o=o||document.querySelector(`[data-br="${e}"]`);const a=o.parentElement,i=W=>o.style.maxWidth=`${W}px`;o.style.maxWidth="";const m=a.clientWidth,z=a.clientHeight;let c=m/2-.25,f=m+.5,w;if(m){for(i(c),c=Math.max(o.scrollWidth,c);c+1{self.__wrap_b(0,+o.dataset.brr,o)})).observe(a)},ht=ve.toString(),_t='(self.CSS&&CSS.supports("text-wrap","balance")?1:2)';function pe(e,t,o){return o&&(o=`self.${P}!=1&&${o}`),me("script",{innerHTML:(e?"":`self.${P}=self.${P}||${_t};self.${F}=${ht};`)+o,nonce:t})}R({name:"BalancerProvider",props:{preferNative:{type:Boolean,required:!1,default:!0},nonce:{type:String,required:!1}},setup(e,{slots:t}){const o=H(()=>e.preferNative);return Ae("BALANCER_PROVIDER",{preferNative:o,hasProvider:!0}),()=>{var a;return[pe(!1,e.nonce),(a=t.default)==null?void 0:a.call(t)]}}});var zt=R({name:"WrapBalancer",props:{as:{type:String,required:!1,default:"span"},ratio:{type:Number,required:!1,default:1},preferNative:{type:Boolean,required:!1,default:!0},nonce:{type:String,required:!1}},setup(e,{slots:t,attrs:o}){const a=e.as,i=o.id||ge(5),m=Me(null),z=qe("BALANCER_PROVIDER",{preferNative:!0,hasProvider:!1}),c=H(()=>{var f;return(f=e.preferNative)!=null?f:B(z.preferNative)});return Ue(()=>{c.value&&typeof self<"u"&&self[P]===1||m.value&&(self[F]=ve)(0,e.ratio,m.value)}),Ve(()=>{if(c.value&&typeof self<"u"&&self[P]===1||!m.value)return;const f=m.value[de];f&&(f.disconnect(),delete m.value[de])}),()=>{var f;return oe(me(a,{...o,"data-brr":e.ratio,ref:m,style:{...o.style,display:"inline-block",verticalAlign:"top",textDecoration:"inherit",textWrap:c.value?"balance":"initial"}},[(f=t.default)==null?void 0:f.call(t),oe(pe(z.hasProvider,e.nonce,`self.${F}(document.currentScript.dataset.ssrId,${e.ratio})`),[[ie,["data-ssr-id",i]]])]),[[ie,["data-br",i]]])}}});/*! + * Original code by Shu Ding + * MIT Licensed, Copyright 2022 Shu Ding, see https://github.com/shuding/react-wrap-balancer/blob/main/LICENSE.md for details + * + * Credits to the team: + * https://github.com/shuding/react-wrap-balancer/blob/main/src/index.tsx + */const yt=e=>(Fe("data-v-7d4dce4c"),e=e(),Ge(),e),bt={class:"absolute left-0 top-0 z-50 flex h-full max-h-[600px] w-full max-w-[1200px] border-b-[1rem] border-ngsek bg-black-900 px-20 pb-14 pt-10"},Et={class:"flex h-full w-full flex-col flex-wrap font-medium text-white"},xt={class:"flex flex-1 flex-wrap items-center"},wt={class:"flex h-full grow flex-col justify-center gap-3"},St={key:0,class:"text-4xl text-black-400"},Ot={class:"text-6xl font-bold leading-tight tracking-[1px]"},Lt={key:1,class:"text-2xl tracking-wider"},Tt={key:2,class:"mt-5"},Ct={key:0,class:"flex flex-1 justify-end"},It={class:"flex w-full justify-between"},Ht={class:"flex items-center gap-3 text-4xl text-black-400"},kt=fe(" By "),Nt=yt(()=>_("div",null,[_("div",{class:"neon inline-block rotate-[-4deg] font-pacifico text-4xl"}," ngseke ")],-1)),$t=R({__name:"OgImageTemplate",props:{title:null,description:null,date:null,tags:null,img:null},setup(e){const t=e,o=H(()=>Ye(t.date)),a=Ke;return(i,m)=>{var f;const z=pt,c=Ze;return x(),We(De,{to:"body"},[_("div",bt,[_("div",Et,[_("div",xt,[_("div",{class:Y(["flex",e.img?"w-3/5":"w-4/5"])},[_("div",wt,[e.date?(x(),S("div",St,N(B(o)),1)):$("",!0),_("h1",Ot,[U(B(zt),{ratio:.5},{default:ue(()=>[fe(N(e.title),1)]),_:1},8,["ratio"])]),e.description?(x(),S("p",Lt,N(e.description),1)):$("",!0),(f=e.tags)!=null&&f.length?(x(),S("div",Tt,[U(z,{list:e.tags,size:"lg"},null,8,["list"])])):$("",!0)])],2),e.img?(x(),S("div",Ct,[U(c,{img:e.img,sizeClassName:"h-[12.1rem] w-[17.6rem]"},null,8,["img"])])):$("",!0)]),_("div",It,[_("div",Ht,[kt,_("span",null,N(B(a)),1)]),Nt])])])])}}});const Mt=G($t,[["__scopeId","data-v-7d4dce4c"]]);function Bt(e){var t=[];if(e.length===0)return"";if(typeof e[0]!="string")throw new TypeError("Url must be a string. Received "+e[0]);if(e[0].match(/^[^/:]+:\/*$/)&&e.length>1){var o=e.shift();e[0]=o+e[0]}e[0].match(/^file:\/\/\//)?e[0]=e[0].replace(/^([^/:]+):\/*/,"$1:///"):e[0]=e[0].replace(/^([^/:]+):\/*/,"$1://");for(var a=0;a0&&(i=i.replace(/^[\/]+/,"")),a0?"?":"")+z.join("&"),m}function Pt(){var e;return typeof arguments[0]=="object"?e=arguments[0]:e=[].slice.call(arguments),Bt(e)}const Rt="og-image";function qt(){const e=Xe(),t=H(()=>{const i=e.name;return typeof i!="string"?null:Pt(Je,Qe,`${i}.png`,`?v=b795911 +`).trim()}),o=H(()=>t.value?[{property:"og:image",content:t.value},{property:"og:image:width",content:1200},{property:"og:image:height",content:600},{name:"twitter:image",content:t.value},{name:"twitter:card",content:"summary_large_image"}]:[]),a=H(()=>Rt in e.query);return{ogImage:t,ogImageHeadMetaList:o,shouldShowOgImage:a}}export{Mt as _,pt as a,At as b,qt as u}; diff --git a/assets/useOgImage-588433c9.css b/assets/useOgImage-588433c9.css new file mode 100644 index 00000000..04032c30 --- /dev/null +++ b/assets/useOgImage-588433c9.css @@ -0,0 +1 @@ +[data-v-eb8991d3] .prose h2,[data-v-eb8991d3] .prose h3,[data-v-eb8991d3] .prose h4{margin-top:3rem;margin-bottom:1rem;font-weight:500}[data-v-eb8991d3] .prose h2:before,[data-v-eb8991d3] .prose h3:before,[data-v-eb8991d3] .prose h4:before{pointer-events:none;margin-top:-5rem;display:block;height:5rem;--tw-content: "";content:var(--tw-content)}[data-v-eb8991d3] .prose h2 a.header-anchor,[data-v-eb8991d3] .prose h3 a.header-anchor,[data-v-eb8991d3] .prose h4 a.header-anchor{margin-left:.25rem;margin-top:-.5rem;display:inline-block;--tw-scale-x: .9;--tw-scale-y: .9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));vertical-align:middle;font-size:.875rem;line-height:1.25rem;opacity:0;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}[data-v-eb8991d3] .prose h2:hover .header-anchor,[data-v-eb8991d3] .prose h3:hover .header-anchor,[data-v-eb8991d3] .prose h4:hover .header-anchor{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));opacity:.8}[data-v-eb8991d3] .prose h2{font-size:1.875rem;line-height:2.25rem}[data-v-eb8991d3] .prose h3{font-size:1.5rem;line-height:2rem}[data-v-eb8991d3] .prose h4{font-size:1.25rem;line-height:1.75rem}[data-v-eb8991d3] .prose p{margin-top:1.5rem;margin-bottom:1rem;line-height:1.75rem;letter-spacing:.2px}[data-v-eb8991d3] .prose p>img{margin-top:1.5rem;margin-bottom:1rem;max-width:min(30rem,100%);margin-left:auto;margin-right:auto;width:auto}[data-v-eb8991d3] .prose blockquote{border-left-width:4px;border-color:#73737380;padding-left:1rem;padding-right:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity))}[data-v-eb8991d3] .prose blockquote p img{margin-left:0;margin-right:0;max-width:100%}[data-v-eb8991d3] .prose ul,[data-v-eb8991d3] .prose ol{margin-top:1.5rem;margin-bottom:1rem;list-style-position:outside}[data-v-eb8991d3] .prose ul>:not([hidden])~:not([hidden]),[data-v-eb8991d3] .prose ol>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}[data-v-eb8991d3] .prose ul,[data-v-eb8991d3] .prose ol{padding-left:1.25rem}[data-v-eb8991d3] .prose ul li,[data-v-eb8991d3] .prose ol li{padding-left:.5rem}[data-v-eb8991d3] .prose ul{list-style-type:disc}[data-v-eb8991d3] .prose ol{list-style-type:decimal}[data-v-eb8991d3] .prose a{text-decoration-line:underline;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media (hover: hover) and (pointer: fine){[data-v-eb8991d3] .prose a:hover{text-decoration-color:#ffd01980}}[data-v-eb8991d3] .prose s,[data-v-eb8991d3] .prose s>a{text-decoration-line:line-through}[data-v-eb8991d3] .prose hr{margin-top:1.5rem;margin-bottom:1.5rem;border-color:currentColor;opacity:.3}[data-v-eb8991d3] .prose a,[data-v-eb8991d3] .prose code{overflow-wrap:break-word}[data-v-eb8991d3] .prose :not(pre)>code{border-radius:.375rem;background-color:#a3a3a333;padding-left:.4rem;padding-right:.4rem}[data-v-eb8991d3] .prose .embed-responsive{margin-top:1.5rem;margin-bottom:1rem;max-width:min(30rem,100%);position:relative;margin-left:auto;margin-right:auto;width:100%;overflow:hidden}[data-v-eb8991d3] .prose .embed-responsive:after{display:block;content:var(--tw-content);padding-top:56.25%}[data-v-eb8991d3] .prose .embed-responsive .embed-responsive-item{position:absolute;top:0;right:0;bottom:0;left:0;height:100%;width:100%}[data-v-eb8991d3] .prose .shiki{margin-top:1.5rem;margin-bottom:1rem;overflow-x:auto;border-radius:.5rem;padding:1rem}[data-v-eb8991d3] .prose .shiki-light{--tw-bg-opacity: 1 !important;background-color:rgb(245 245 245 / var(--tw-bg-opacity))!important}[data-v-eb8991d3] .prose .shiki-dark{display:none}[data-v-eb8991d3] .prose.dark blockquote{--tw-text-opacity: 1;color:rgb(163 163 163 / var(--tw-text-opacity))}[data-v-eb8991d3] .prose.dark .shiki-light{display:none}[data-v-eb8991d3] .prose.dark .shiki-dark{display:block}.neon[data-v-7d4dce4c]{color:#fff;text-shadow:0 0 6px rgba(255,255,255,.92),0 0 30px rgba(255,255,255,.34),0 0 12px rgba(255,208,25,.52),0 0 21px rgba(255,208,25,.5),0 0 34px rgba(255,208,25,.7)} diff --git a/assets/useReadHistory-d98c4b5d.js b/assets/useReadHistory-d98c4b5d.js new file mode 100644 index 00000000..530cfca9 --- /dev/null +++ b/assets/useReadHistory-d98c4b5d.js @@ -0,0 +1 @@ +import{z as r,f as c,q as i}from"./app-033a95d9.js";const u="readHistory";function y(){const e=r(u,{}),t=s=>{s in e||(e.value[s]=+new Date)},{projectMap:o}=c(),a=i(()=>Object.keys(o.value).every(s=>s in e.value));return{readHistory:e,pushReadHistory:t,isReadAll:a,clearReadHistory:()=>{e.value={}}}}export{y as u}; diff --git a/assets/valueOf-956b83dd.png b/assets/valueOf-956b83dd.png new file mode 100644 index 00000000..d168b030 Binary files /dev/null and b/assets/valueOf-956b83dd.png differ diff --git a/assets/versatile-npm-d3f5c477.js b/assets/versatile-npm-d3f5c477.js new file mode 100644 index 00000000..51528eb4 --- /dev/null +++ b/assets/versatile-npm-d3f5c477.js @@ -0,0 +1 @@ +import{d as a,v as r,o as i,a as p,h as e,j as t,k as s,A as c}from"./app-033a95d9.js";const h="/assets/1400x560-02b6cc6e.png",l="/assets/demo-222ae85f.gif",d="/assets/screenshot-2-9726f1a7.png",m="/assets/screenshot-1-81b8fb35.png",g="/assets/logo-candidates-4f73c7bd.png",_={class:"markdown-body"},b=e("p",null,[e("img",{src:h,alt:""})],-1),f={id:"%E7%B0%A1%E4%BB%8B",tabindex:"-1"},u=s("簡介 "),k={class:"header-anchor",href:"#%E7%B0%A1%E4%BB%8B"},v=c('

Versatile Npm 是 Google Chrome 擴充功能,使用 Vue 3 + TypeScript + Chrome Extension API + Vuetify 建構。

Demonstration

這個擴充功能可以讓使用者在 npm 套件頁面的右側欄加入 yarnpnpm 等安裝指令,方便開發者一鍵複製。

Screenshot 2

另外也提供設定 UI 供使用者自定義安裝指令,執行新增、刪除、編輯和排序操作。

Screenshot 1

',6),j={id:"logo",tabindex:"-1"},V=s("Logo "),y={class:"header-anchor",href:"#logo"},B=c('

這款 App 的 Logo 是透過 DALL·E 3 產生的,輸入 prompt 基本上包含 穿著帽 T 的水豚包裹紅色badge stylesport mascot style,最後透過 macOS 內建的「移除背景」功能去背。這過程幾乎不費吹灰之力,完全無需任何專業繪圖軟體。

其他候選 Logo 如下:
Logo Candidates

',2),x={id:"demo",tabindex:"-1"},L=s("Demo "),N={class:"header-anchor",href:"#demo"},T=e("a",{href:"https://chromewebstore.google.com/detail/versatile-npm/jahejogdoffpehfhkhbpjblnlhghjnje?hl=zh-TW",target:"_blank"},[e("img",{src:"https://storage.googleapis.com/web-dev-uploads/image/WlD8wC6g8khYWPJUsQceQkhXSlv1/UV4C4ybeBTsZt43U4xis.png"})],-1),S=e("br",null,null,-1),w=e("iframe",{src:"https://ghbtns.com/github-btn.html?user=ngseke&repo=versatile-npm&type=star&count=false",frameborder:"0",scrolling:"0",width:"150",height:"20"},null,-1),W={title:"Versatile Npm",briefDescription:"自訂 Npm 安裝指令瀏覽器擴充功能",githubLink:"https://github.com/ngseke/versatile-npm",demoLink:"https://chromewebstore.google.com/detail/versatile-npm/jahejogdoffpehfhkhbpjblnlhghjnje?hl=zh-TW",period:["2023/11","2023/11"],cover:"/img/project-cover/versatile-npm.png",tags:["Vue","TypeScript","Vuetify","npm"]},U="",z=a({__name:"versatile-npm",setup(C,{expose:n}){return n({frontmatter:{title:"Versatile Npm",briefDescription:"自訂 Npm 安裝指令瀏覽器擴充功能",githubLink:"https://github.com/ngseke/versatile-npm",demoLink:"https://chromewebstore.google.com/detail/versatile-npm/jahejogdoffpehfhkhbpjblnlhghjnje?hl=zh-TW",period:["2023/11","2023/11"],cover:"/img/project-cover/versatile-npm.png",tags:["Vue","TypeScript","Vuetify","npm"]},excerpt:void 0}),(E,A)=>{const o=r("icon-link");return i(),p("div",_,[b,e("h2",f,[u,e("a",k,[t(o)])]),v,e("h2",j,[V,e("a",y,[t(o)])]),B,e("h2",x,[L,e("a",N,[t(o)])]),T,S,w])}}});export{z as default,U as excerpt,W as frontmatter}; diff --git a/assets/vite-vue-ts-eslint-setup-1bf5e98d.js b/assets/vite-vue-ts-eslint-setup-1bf5e98d.js new file mode 100644 index 00000000..27fab800 --- /dev/null +++ b/assets/vite-vue-ts-eslint-setup-1bf5e98d.js @@ -0,0 +1,165 @@ +import{d as p,v as t,o as c,a as i,h as s,j as e,k as n,A as o}from"./app-033a95d9.js";const r="/assets/output-parserOptions-project-error-ceeaa787.png",d="/assets/output-parserOptions-project-without-error-74ff031b.png",u="/assets/tsconfig-include-a4dd9125.png",y="/assets/command-reload-window-955490ff.png",A="/assets/vscode-linting-result-f315bd6a.png",h="/assets/vue-file-error-73be3796.png",E="/assets/move-object-properties-99f2f466.gif",g="/assets/vscode-auto-fix-on-save-445e3f9d.gif",f="/assets/terminal-lint-09df797c.png",B="/assets/eslint-automatically-fixable-4b63f152.png",D={class:"markdown-body"},q=s("blockquote",null,[s("p",null,"以下使用 pnpm 作為套件管理工具,與 npm 或 yarn 的指令會有些許差異,請查看隨附的官方文件")],-1),_={id:"tl%3Bdr",tabindex:"-1"},k=n("TL;DR "),v={class:"header-anchor",href:"#tl%3Bdr"},C=s("p",null,[n("完整配置範例:"),s("a",{href:"https://github.com/ngseke/vite-vue-ts-eslint-example"},"https://github.com/ngseke/vite-vue-ts-eslint-example")],-1),m={id:"%F0%9F%8F%97%EF%B8%8F-%E9%80%8F%E9%81%8E-vite-%E5%AE%98%E6%96%B9%E7%9A%84-preset-%E6%96%B0%E5%BB%BA%E5%B0%88%E6%A1%88",tabindex:"-1"},F=n("🏗️ 透過 Vite 官方的 Preset 新建專案 "),b={class:"header-anchor",href:"#%F0%9F%8F%97%EF%B8%8F-%E9%80%8F%E9%81%8E-vite-%E5%AE%98%E6%96%B9%E7%9A%84-preset-%E6%96%B0%E5%BB%BA%E5%B0%88%E6%A1%88"},j=o(`

Scaffolding Your First Vite Project

pnpm create vite my-vue-app --template vue-ts
+
pnpm create vite my-vue-app --template vue-ts
+

移動到該目錄並安裝相依

cd my-vue-app
+pnpm i
+
cd my-vue-app
+pnpm i
+
`,4),x={id:"%F0%9F%8F%97%EF%B8%8F-%E5%88%9D%E5%A7%8B%E5%8C%96-eslint",tabindex:"-1"},w=n("🏗️ 初始化 ESLint "),S={class:"header-anchor",href:"#%F0%9F%8F%97%EF%B8%8F-%E5%88%9D%E5%A7%8B%E5%8C%96-eslint"},V=o(`

Getting Started with ESLint - Quick start

pnpm create @eslint/config
+
pnpm create @eslint/config
+

依序回答以下問題後,便會在根目錄新建 .eslintrc.cjs

✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · vue
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · standard-with-typescript
✔ What format do you want your config file to be in? · JavaScript
✔ Would you like to install them now? · No / Yes
✔ Which package manager do you want to use? · pnpm

`,4),O={id:"%F0%9F%98%B5-%E8%99%95%E7%90%86-vscode-output-%E7%9A%84-eslint-%E7%9A%84%E5%95%8F%E9%A1%8C",tabindex:"-1"},L=n("😵 處理 VSCode Output 的 ESLint 的問題 "),T={class:"header-anchor",href:"#%F0%9F%98%B5-%E8%99%95%E7%90%86-vscode-output-%E7%9A%84-eslint-%E7%9A%84%E5%95%8F%E9%A1%8C"},P=o('

雖然 ESLint 已初始化完成,但你會發現它尚未正常運作。例如試著在任意 .ts.vue 檔隨便加多餘的空格,卻看不到預期的 error 或 warning 的波浪底線。

查看 Output 會看到以下錯誤訊息:

An unexpected error occurred:
Error: Error while loading rule ‘@typescript-eslint/dot-notation’: You have used a rule which requires parserServices to be generated. You must therefore provide a value for the “parserOptions.project” property for @typescript-eslint/parser.

根據錯誤訊息的描述,得知需要在 .eslintrc.cjs 補上 parserOptions.project 並指明 parserOptions.parser

// .eslintrc.cjs
+module.exports = {
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
++       project: ['./tsconfig.json', './tsconfig.node.json'],
++       parser: '@typescript-eslint/parser',
+    },
+}
+
// .eslintrc.cjs
+module.exports = {
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
++       project: ['./tsconfig.json', './tsconfig.node.json'],
++       parser: '@typescript-eslint/parser',
+    },
+}
+

再次查看 Output 錯誤訊息已消失

Q: 為什麼是 parserOptions.parser 而非 parser
A: 根據官方文件說明,若寫在 parser 會把 vue-eslint-parser 覆蓋掉而無法正常地 lint .vue 檔。所以當有自訂的 parser 時(例如 @typescript-eslint/parser),必須把它移入 parserOptions

',9),W={id:"%F0%9F%98%B5-%E8%99%95%E7%90%86%E6%A0%B9%E7%9B%AE%E9%8C%84%E6%AA%94-tsconfig-include-%E7%9A%84%E5%95%8F%E9%A1%8C",tabindex:"-1"},N=n("😵 處理根目錄檔 TSConfig include 的問題 "),Y={class:"header-anchor",href:"#%F0%9F%98%B5-%E8%99%95%E7%90%86%E6%A0%B9%E7%9B%AE%E9%8C%84%E6%AA%94-tsconfig-include-%E7%9A%84%E5%95%8F%E9%A1%8C"},R=o('

雖然 Output 錯誤訊息已消失,但這時用 VSCode 開啟在根目錄的 .ts.cjs 檔,例如 vite.config.ts.eslintrc.cjs,會發現它們在開頭都多了紅色的波浪底線:

Parsing error: ESLint was configured to run on <tsconfigRootDir>/.eslintrc.cjs using parserOptions.project: /users/sean/my-vue-app/tsconfig.json
However, that TSConfig does not include this file. Either:

根據錯誤訊息得知,我們必須在 tsconfig.node.jsonincludes 中手動加入這些檔案:

// tsconfig.node.json
+{
+  // ...
+  "include": [
+    "vite.config.ts",
++   ".eslintrc.cjs"
+  ],
+}
+
// tsconfig.node.json
+{
+  // ...
+  "include": [
+    "vite.config.ts",
++   ".eslintrc.cjs"
+  ],
+}
+

日後若是在根目錄有新增那些在 Node 環境執行的,而不會被實際打包進專案的設定檔,例如 Tailwind CSS 的設定檔 tailwind.config.ts,也要記得手動加入進去。

接著重新載入 VSCode 視窗

叫出指令視窗 ⌘ + shift + P → 輸入 Developer: Reload Window

打開 .eslintrc.cjs 可以看到 ESLint 終於正常運作了,它開始確實根據 eslint-config-standard-with-typescript 的預設規則進行檢查,例如多餘的引號、字串應為單引號和縮排應為 2 格空格等。

',11),H={id:"%F0%9F%98%B5-%E8%99%95%E7%90%86-.vue-%E6%AA%94%E7%9A%84-non-standard-%E5%95%8F%E9%A1%8C",tabindex:"-1"},I=n("😵 處理 "),Q=s("code",{class:""},".vue",-1),G=n(" 檔的 non-standard 問題 "),J={class:"header-anchor",href:"#%F0%9F%98%B5-%E8%99%95%E7%90%86-.vue-%E6%AA%94%E7%9A%84-non-standard-%E5%95%8F%E9%A1%8C"},z=o('

打開任意 .vue 檔會發現它們也出現了錯誤訊息:

Parsing error: ESLint was configured to run on <tsconfigRootDir>/src/App.vue using parserOptions.project: /users/sean/my-vue-app/tsconfig.json
The extension for the file (.vue) is non-standard. You should add parserOptions.extraFileExtensions to your config.

根據錯誤訊息的提示在 .eslintrc.cjs 加入 parserOptions.extraFileExtensions 後,再次重新載入 VSCode 視窗即可。

// .eslintrc.cjs
+{
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
+        project: ['./tsconfig.json', './tsconfig.node.json'],
+        parser: '@typescript-eslint/parser',
++       extraFileExtensions: ['.vue']
+    },
+}
+
// .eslintrc.cjs
+{
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
+        project: ['./tsconfig.json', './tsconfig.node.json'],
+        parser: '@typescript-eslint/parser',
++       extraFileExtensions: ['.vue']
+    },
+}
+
`,5),K={id:"%F0%9F%98%B5-%E8%B7%B3%E9%81%8E%E6%AA%A2%E6%9F%A5%E6%9F%90%E4%BA%9B%E6%AA%94%E6%A1%88",tabindex:"-1"},M=n("😵 跳過檢查某些檔案 "),U={class:"header-anchor",href:"#%F0%9F%98%B5-%E8%B7%B3%E9%81%8E%E6%AA%A2%E6%9F%A5%E6%9F%90%E4%BA%9B%E6%AA%94%E6%A1%88"},X={id:"dist%2F",tabindex:"-1"},Z=s("code",{class:""},"dist/",-1),$=n(),ss={class:"header-anchor",href:"#dist%2F"},ns=o(`

我們沒必要檢查建構好的已醜化和壓縮的檔案,因此可以在 .eslintrc.cjsignorePatterns 排除掉整個 dist 目錄:

// .eslintrc.cjs
+module.exports = {
+    // ...
++   ignorePatterns: ['dist'],
+}
+
// .eslintrc.cjs
+module.exports = {
+    // ...
++   ignorePatterns: ['dist'],
+}
+
`,2),as={id:"vite-env.d.ts",tabindex:"-1"},es=s("code",{class:""},"vite-env.d.ts",-1),os=n(),ls={class:"header-anchor",href:"#vite-env.d.ts"},ps=o(`

按照預設規則,vite-env.d.ts 會違反 @typescript-eslint/triple-slash-reference 這條規則,建議可以在 vite-env.d.ts 的開頭加上註解 // eslint-disable-next-line ... 來跳過檢查:

// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference types="vite/client" />
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference types="vite/client" />
+
`,2),ts={id:"%E2%9C%85-%E5%A4%A7%E5%8A%9F%E5%91%8A%E6%88%90",tabindex:"-1"},cs=n("✅ 大功告成 "),is={class:"header-anchor",href:"#%E2%9C%85-%E5%A4%A7%E5%8A%9F%E5%91%8A%E6%88%90"},rs=o(`

到此為止 ESLint 應該就可以順利的運作,接著你可以更進一步根據個人或團隊的風格和偏好調整 rules,開啟、關閉或調整某些規則。

例如我總是習慣微調 @typescript-eslint/comma-dangle(行末逗號)的設定,讓最後一項依然保留逗號,這樣就可以更方便調整物件成員的順序:

module.exports = {
+  // ...
+  rules: {
+    // ...
+    '@typescript-eslint/comma-dangle': ['error', {
+      arrays: 'always-multiline',
+      objects: 'always-multiline',
+      imports: 'always-multiline',
+      exports: 'always-multiline',
+      functions: 'only-multiline',
+    }],
+  },
+}
+
module.exports = {
+  // ...
+  rules: {
+    // ...
+    '@typescript-eslint/comma-dangle': ['error', {
+      arrays: 'always-multiline',
+      objects: 'always-multiline',
+      imports: 'always-multiline',
+      exports: 'always-multiline',
+      functions: 'only-multiline',
+    }],
+  },
+}
+

',4),ds={id:"%E2%9C%A8-%E8%A8%AD%E5%AE%9A-vscode-%E5%AD%98%E6%AA%94%E6%99%82%E8%87%AA%E5%8B%95%E6%8E%92%E7%89%88",tabindex:"-1"},us=n("✨ 設定 VSCode 存檔時自動排版 "),ys={class:"header-anchor",href:"#%E2%9C%A8-%E8%A8%AD%E5%AE%9A-vscode-%E5%AD%98%E6%AA%94%E6%99%82%E8%87%AA%E5%8B%95%E6%8E%92%E7%89%88"},As=o(`

你還可以讓開發體驗變的更舒適。

打開 .vscode/settings.json,若原本沒有這個檔案可以手動建立一個,加入以下設定:

{
+  // ...
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  },
+}
+
{
+  // ...
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  },
+}
+

回到剛才滿江紅的 .eslintrc.cjs 測試看看,在手動存檔(⌘ + S)後就會自動排版和修正錯誤:

',5),hs={id:"%E2%9C%A8-%E9%80%8F%E9%81%8E-script-%E6%AA%A2%E6%9F%A5%E9%8C%AF%E8%AA%A4%E6%88%96%E6%98%AF%E8%87%AA%E5%8B%95%E4%BF%AE%E5%BE%A9%E9%8C%AF%E8%AA%A4",tabindex:"-1"},Es=n("✨ 透過 script 檢查錯誤或是自動修復錯誤 "),gs={class:"header-anchor",href:"#%E2%9C%A8-%E9%80%8F%E9%81%8E-script-%E6%AA%A2%E6%9F%A5%E9%8C%AF%E8%AA%A4%E6%88%96%E6%98%AF%E8%87%AA%E5%8B%95%E4%BF%AE%E5%BE%A9%E9%8C%AF%E8%AA%A4"},fs=s("p",null,[n("儘管在 VSCode 中的檢查和錯誤提示有助於得到即時反饋,但這些提示終究是 "),s("strong",null,"「消極的」"),n(" 。如果開發者使用別款 IDE,或只是單純對提示視若無睹,他們仍然可以輕易地違反這些規則,並提交不符合規則的程式碼。")],-1),Bs=s("p",null,"因此我們還需要透過 script 的方式來真正的執行規則檢查。",-1),Ds={id:"%E7%94%A8%E6%8C%87%E4%BB%A4%E6%AA%A2%E6%9F%A5%E9%8C%AF%E8%AA%A4",tabindex:"-1"},qs=n("用指令檢查錯誤 "),_s={class:"header-anchor",href:"#%E7%94%A8%E6%8C%87%E4%BB%A4%E6%AA%A2%E6%9F%A5%E9%8C%AF%E8%AA%A4"},ks=o(`

打開 package.json,在 scripts 中加入指令 lint,其中 --ext 的後面是想要檢查的副檔名:

// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
++   "lint": "npx eslint . --ext .ts,.js,.cjs,.vue"
+  },
+}
+
// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
++   "lint": "npx eslint . --ext .ts,.js,.cjs,.vue"
+  },
+}
+

接著在 terminal 測試執行效果

pnpm run lint
+
pnpm run lint
+

可以看到它列出了所有不符合設定規則的 error 和 warning,並且回傳 exit code 1

這表示你就可以將這條指令整合進你的部署流程中,例如:

  1. 搭配 husky:使用 husky 可以在每次 commit 前自動執行 pnpm run lint。如果程式碼不符合規則就不給 commit,迫使開發者修復錯誤後再進行 commit,確保 code base 的風格始終維持一致
  2. CI/CD:你可以將 pnpm run lint 加入 CI/CD 流程,這樣在每次部署前都會自動進行代碼檢查。若檢查失敗,部署流程就會自動中斷,讓開發者不得不修正錯誤
',8),vs={id:"%E7%94%A8%E6%8C%87%E4%BB%A4%E8%87%AA%E5%8B%95%E4%BF%AE%E5%BE%A9%E9%8C%AF%E8%AA%A4",tabindex:"-1"},Cs=n("用指令自動修復錯誤 "),ms={class:"header-anchor",href:"#%E7%94%A8%E6%8C%87%E4%BB%A4%E8%87%AA%E5%8B%95%E4%BF%AE%E5%BE%A9%E9%8C%AF%E8%AA%A4"},Fs=o(`

打開 package.json,在 scripts 中加入以下指令 lint:fix

// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
+    "lint": "npx eslint . --ext .ts,.js,.cjs,.vue",
++   "lint:fix": "npx eslint --fix . --ext .ts,.js,.cjs,.vue"
+  },
+}
+
// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
+    "lint": "npx eslint . --ext .ts,.js,.cjs,.vue",
++   "lint:fix": "npx eslint --fix . --ext .ts,.js,.cjs,.vue"
+  },
+}
+

接著在 terminal 測試執行效果

pnpm run lint:fix
+
pnpm run lint:fix
+

順利的話「可自動修正(automatically fixable)」的那些規則都會被自動修正,也就是在 Rule 列表有 🔧 符號的那些項目。

',6),bs={id:"eslint-%E5%8D%B3%E5%B0%87%E6%A3%84%E7%94%A8%E3%80%8C%E6%8E%92%E7%89%88%E3%80%8D%E8%A6%8F%E5%89%87",tabindex:"-1"},js=n("ESLint 即將棄用「排版」規則 "),xs={class:"header-anchor",href:"#eslint-%E5%8D%B3%E5%B0%87%E6%A3%84%E7%94%A8%E3%80%8C%E6%8E%92%E7%89%88%E3%80%8D%E8%A6%8F%E5%89%87"},ws=o('

ESLint 在 2023 年 10 月宣布將棄用排版(Formatting)規則,也就是棄用那些跟空格、縮排、換行、單/雙引號、分號等相關規則。而其餘分類下的規則不受影響,例如強制使用嚴格等於 ===eqeqeq)、強制命名小駝峰(camelcase)等。

未來若想繼續透過 ESLint 而非 Prettier 來排版程式碼,可以考慮搭配 ESLint Stylistic 來達成一樣的效果。這個 plugin 將會繼續接棒,維護這些被棄用的規則。

',2),Ss={id:"%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99",tabindex:"-1"},Vs=n("參考資料 "),Os={class:"header-anchor",href:"#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99"},Ls=s("p",null,[s("a",{href:"https://github.com/vitejs/vite/issues/13739#issuecomment-1641380518"},"https://github.com/vitejs/vite/issues/13739#issuecomment-1641380518"),s("br"),s("a",{href:"https://juejin.cn/post/7126043888573218823"},"https://juejin.cn/post/7126043888573218823")],-1),Ys={title:"建立 Vite + Vue + TypeScript + ESLint 專案可能會遇到的坑",date:"2023/10/03",tags:["Vite","Vue","TypeScript","ESLint"],original:"https://hackmd.io/@xq/vite-vue-ts-eslint-setup"},Rs="",Hs=p({__name:"vite-vue-ts-eslint-setup",setup(Ts,{expose:l}){return l({frontmatter:{title:"建立 Vite + Vue + TypeScript + ESLint 專案可能會遇到的坑",date:"2023/10/03",tags:["Vite","Vue","TypeScript","ESLint"],original:"https://hackmd.io/@xq/vite-vue-ts-eslint-setup"},excerpt:void 0}),(Ps,Ws)=>{const a=t("icon-link");return c(),i("div",D,[q,s("h2",_,[k,s("a",v,[e(a)])]),C,s("h2",m,[F,s("a",b,[e(a)])]),j,s("h2",x,[w,s("a",S,[e(a)])]),V,s("h2",O,[L,s("a",T,[e(a)])]),P,s("h2",W,[N,s("a",Y,[e(a)])]),R,s("h2",H,[I,Q,G,s("a",J,[e(a)])]),z,s("h2",K,[M,s("a",U,[e(a)])]),s("h3",X,[Z,$,s("a",ss,[e(a)])]),ns,s("h3",as,[es,os,s("a",ls,[e(a)])]),ps,s("h2",ts,[cs,s("a",is,[e(a)])]),rs,s("h2",ds,[us,s("a",ys,[e(a)])]),As,s("h2",hs,[Es,s("a",gs,[e(a)])]),fs,Bs,s("h3",Ds,[qs,s("a",_s,[e(a)])]),ks,s("h3",vs,[Cs,s("a",ms,[e(a)])]),Fs,s("h2",bs,[js,s("a",xs,[e(a)])]),ws,s("h2",Ss,[Vs,s("a",Os,[e(a)])]),Ls])}}});export{Hs as default,Rs as excerpt,Ys as frontmatter}; diff --git a/assets/vscode-auto-fix-on-save-445e3f9d.gif b/assets/vscode-auto-fix-on-save-445e3f9d.gif new file mode 100644 index 00000000..5ce38df8 Binary files /dev/null and b/assets/vscode-auto-fix-on-save-445e3f9d.gif differ diff --git a/assets/vscode-linting-result-f315bd6a.png b/assets/vscode-linting-result-f315bd6a.png new file mode 100644 index 00000000..7445524f Binary files /dev/null and b/assets/vscode-linting-result-f315bd6a.png differ diff --git a/assets/vue-file-error-73be3796.png b/assets/vue-file-error-73be3796.png new file mode 100644 index 00000000..69d67f6d Binary files /dev/null and b/assets/vue-file-error-73be3796.png differ diff --git a/assets/webpack-config-esm-320be40c.js b/assets/webpack-config-esm-320be40c.js new file mode 100644 index 00000000..6ba505f2 --- /dev/null +++ b/assets/webpack-config-esm-320be40c.js @@ -0,0 +1,19 @@ +import{d as c,v as t,o as p,a as r,h as s,j as n,k as e,A as o}from"./app-033a95d9.js";const i={class:"markdown-body"},d={id:"%E6%AD%A5%E9%A9%9F",tabindex:"-1"},h=e("步驟 "),b={class:"header-anchor",href:"#%E6%AD%A5%E9%A9%9F"},_={id:"%E5%AE%89%E8%A3%9D-%40babel%2Fregister-%E8%88%87-babel-preset-env",tabindex:"-1"},k=e("安裝 "),A=s("code",{class:""},"@babel/register",-1),E=e(" 與 "),f=s("code",{class:""},"babel-preset-env",-1),y=e(),g={class:"header-anchor",href:"#%E5%AE%89%E8%A3%9D-%40babel%2Fregister-%E8%88%87-babel-preset-env"},u=o(`
npm install -D @babel/core @babel/register babel-preset-env
+
npm install -D @babel/core @babel/register babel-preset-env
+
`,1),B={id:"%E8%A8%AD%E5%AE%9A-.babelrc",tabindex:"-1"},D=e("設定 "),m=s("code",{class:""},".babelrc",-1),v=e(),w={class:"header-anchor",href:"#%E8%A8%AD%E5%AE%9A-.babelrc"},q=o(`
{
+  "presets": [
++   "@babel/preset-env"
+  ],
+}
+
+
{
+  "presets": [
++   "@babel/preset-env"
+  ],
+}
+
+
`,1),x={id:"%E6%9B%B4%E6%96%B0-webpack-%E8%A8%AD%E5%AE%9A",tabindex:"-1"},C=e("更新 webpack 設定 "),F={class:"header-anchor",href:"#%E6%9B%B4%E6%96%B0-webpack-%E8%A8%AD%E5%AE%9A"},j=o(`
  1. 將原 webpack.config.js 更名為 webpack.config.babel.js
  2. 在專案根目錄建立 webpack.config.js
    require('@babel/register')
    +module.exports = require('./webpack.config.babel.js')
    +
    require('@babel/register')
    +module.exports = require('./webpack.config.babel.js')
    +
`,1),N={id:"%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99",tabindex:"-1"},V=e("參考資料 "),S={class:"header-anchor",href:"#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99"},M=s("ul",null,[s("li",null,[s("a",{href:"https://stackoverflow.com/a/43165210/12970551"},"https://stackoverflow.com/a/43165210/12970551")]),s("li",null,[s("a",{href:"https://stackoverflow.com/a/52092788/12970551"},"https://stackoverflow.com/a/52092788/12970551")])],-1),H={title:"在 webpack.config.js 裡使用 ESM 的 import 語法",date:"2022/02/20",tags:["Webpack"],original:"https://hackmd.io/@xq/webpack-config-esm"},I="",J=c({__name:"webpack-config-esm",setup(W,{expose:l}){return l({frontmatter:{title:"在 webpack.config.js 裡使用 ESM 的 import 語法",date:"2022/02/20",tags:["Webpack"],original:"https://hackmd.io/@xq/webpack-config-esm"},excerpt:void 0}),(T,z)=>{const a=t("icon-link");return p(),r("div",i,[s("h2",d,[h,s("a",b,[n(a)])]),s("h3",_,[k,A,E,f,y,s("a",g,[n(a)])]),u,s("h3",B,[D,m,v,s("a",w,[n(a)])]),q,s("h3",x,[C,s("a",F,[n(a)])]),j,s("h2",N,[V,s("a",S,[n(a)])]),M])}}});export{J as default,I as excerpt,H as frontmatter}; diff --git a/assets/wikipedia-link-converter-5743973b.js b/assets/wikipedia-link-converter-5743973b.js new file mode 100644 index 00000000..474a740b --- /dev/null +++ b/assets/wikipedia-link-converter-5743973b.js @@ -0,0 +1 @@ +import{d as n,v as r,o as c,a as _,h as e,j as s,k as t}from"./app-033a95d9.js";const i="/assets/before-7a436972.png",d="/assets/after-97d65a70.png",h={class:"markdown-body"},l={id:"%E8%85%B3%E6%9C%AC%E9%80%A3%E7%B5%90",tabindex:"-1"},p=t("腳本連結 "),m={class:"header-anchor",href:"#%E8%85%B3%E6%9C%AC%E9%80%A3%E7%B5%90"},E=e("p",null,[e("a",{href:"https://gist.github.com/ngseke/5d0f9ef02aa320e969768aaa8e5d888a"},"https://gist.github.com/ngseke/5d0f9ef02aa320e969768aaa8e5d888a")],-1),f={id:"%E8%AA%AA%E6%98%8E",tabindex:"-1"},g=t("說明 "),k={class:"header-anchor",href:"#%E8%AA%AA%E6%98%8E"},B=e("p",null,"不知道從何時開始,Google 搜尋結果的維基百科連結,在不知不覺間都變成了行動版本。然而行動版在筆電上總有一股違和感,也可能是我早已看習慣舊版的了。",-1),u=e("p",null,[t("於是我寫了一段簡單的 "),e("a",{href:"https://www.tampermonkey.net/"},"Tampermonkey"),t(" 腳本,用來轉換 Google 搜尋結果中所有的維基百科連結,將行動版轉換成桌面版,也就是把網址的 "),e("code",{class:""},".m"),t(" 部分給去掉。")],-1),A={id:"%E8%BD%89%E6%8F%9B%E7%B5%90%E6%9E%9C",tabindex:"-1"},b=t("轉換結果 "),x={class:"header-anchor",href:"#%E8%BD%89%E6%8F%9B%E7%B5%90%E6%9E%9C"},C=e("p",null,[e("strong",null,"Before:"),e("br"),e("img",{src:i,alt:"before"})],-1),v=e("p",null,[e("strong",null,"After:"),e("br"),e("img",{src:d,alt:"after"})],-1),N={title:"還給你 Google 搜尋結果的桌面版維基百科",date:"2022/05/27",tags:["Tampermonkey"]},V="",D=n({__name:"wikipedia-link-converter",setup(w,{expose:a}){return a({frontmatter:{title:"還給你 Google 搜尋結果的桌面版維基百科",date:"2022/05/27",tags:["Tampermonkey"]},excerpt:void 0}),(y,G)=>{const o=r("icon-link");return c(),_("div",h,[e("h2",l,[p,e("a",m,[s(o)])]),E,e("h2",f,[g,e("a",k,[s(o)])]),B,u,e("h2",A,[b,e("a",x,[s(o)])]),C,v])}}});export{D as default,V as excerpt,N as frontmatter}; diff --git a/assets/youtube-cc-menu-f1682830.png b/assets/youtube-cc-menu-f1682830.png new file mode 100644 index 00000000..23880fd2 Binary files /dev/null and b/assets/youtube-cc-menu-f1682830.png differ diff --git a/blog.html b/blog.html new file mode 100644 index 00000000..ccad0465 --- /dev/null +++ b/blog.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Blog | ngseke + + + + + + \ No newline at end of file diff --git a/blog/bootstrap-in-nuxt.html b/blog/bootstrap-in-nuxt.html new file mode 100644 index 00000000..643810d1 --- /dev/null +++ b/blog/bootstrap-in-nuxt.html @@ -0,0 +1,140 @@ + + + + + + + + + + + + + 在 Nuxt 專案穩穩地匯入 Bootstrap | ngseke + +
  • Nuxt
  • Vue
  • Bootstrap

在 Nuxt 專案穩穩地匯入 Bootstrap

  • Nov 21, 2021
  • by Sean Huang

此筆記指的是純 bootstrap,與 bootstrap-vue 無關。

下文出現的 BS 是 Bootstrap 的簡寫

🌟 這篇筆記的重點有哪些?

與直接匯入編譯過並打包好的 css 相比,此作法:

  1. 保留自定義樣式的能力,優雅地從根源自定義主題,免去用層層的自訂 class 複寫
  2. 避免組件充斥著 @media (min-width: 576px) { ... } 諸如此類的魔法數字
  3. 節省專案建構大小

🔧 安裝 Bootstrap

npm i bootstrap
+
npm i bootstrap
+

🔧 安裝 @nuxtjs/style-resources

npm i @nuxtjs/style-resources
+
npm i @nuxtjs/style-resources
+

📃 建立 _bootstrap.sass

建立 assets/sass/style-resources/_bootstrap.sass(檔名隨意)

在裡面匯入 BS 的源 SCSS 檔案,這三行是必須的。

// _bootstrap.sass
+
+@import "~bootstrap/scss/functions"
+@import "~bootstrap/scss/variables"
+@import "~bootstrap/scss/mixins"
+
// _bootstrap.sass
+
+@import "~bootstrap/scss/functions"
+@import "~bootstrap/scss/variables"
+@import "~bootstrap/scss/mixins"
+

https://getbootstrap.com/docs/4.6/getting-started/theming/#importing

📃 建立 _custom.sass

建立 assets/sass/style-resources/_custom.sass(檔名隨意)。

此檔用來複寫 BS 預設變數。

// _custom.sass
+
+// 🌰 舉例:
+
+// 將「primary」換成黃色
+$theme-colors: (primary: #ffd019)
+
+// 將連結換成黃色,並移除 hover 的底線
+$link-color: #ffd019
+$link-hover-decoration: none
+
+// 重新定義斷點大小
+$grid-breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)
+
+/* ... */
+
// _custom.sass
+
+// 🌰 舉例:
+
+// 將「primary」換成黃色
+$theme-colors: (primary: #ffd019)
+
+// 將連結換成黃色,並移除 hover 的底線
+$link-color: #ffd019
+$link-hover-decoration: none
+
+// 重新定義斷點大小
+$grid-breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)
+
+/* ... */
+

更多可供複寫的變數都列在此處:
https://github.com/twbs/bootstrap/blob/main/scss/_variables.scss

📃 建立 style.sass

建立 assets/sass/style.sass(檔名隨意)

根據各自需求匯入 BS 元件,這些元件將會套用前面設定的主題變數。

// style.sass
+
+// 👍 推薦的元件
+@import "~bootstrap/scss/reboot"
+@import "~bootstrap/scss/utilities"
+@import "~bootstrap/scss/grid"
+@import "~bootstrap/scss/type"
+
+// 其他常用元件
+@import "~bootstrap/scss/images"
+@import "~bootstrap/scss/nav"
+@import "~bootstrap/scss/navbar"
+
+/* ... */
+
// style.sass
+
+// 👍 推薦的元件
+@import "~bootstrap/scss/reboot"
+@import "~bootstrap/scss/utilities"
+@import "~bootstrap/scss/grid"
+@import "~bootstrap/scss/type"
+
+// 其他常用元件
+@import "~bootstrap/scss/images"
+@import "~bootstrap/scss/nav"
+@import "~bootstrap/scss/navbar"
+
+/* ... */
+

可用的元件列表及用途請參考此處:
https://github.com/twbs/bootstrap/tree/main/scss

✏️ 配置 nuxt.config.js

export default {
+  /* ... */
+
+  styleResources: {
+    sass: [
+      './assets/sass/style-resources/_custom.sass',
+      './assets/sass/style-resources/_bootstrap.sass',
+    ],
+  },
+
+  css: [
+    './assets/sass/style.sass',
+  ],
+}
+
export default {
+  /* ... */
+
+  styleResources: {
+    sass: [
+      './assets/sass/style-resources/_custom.sass',
+      './assets/sass/style-resources/_bootstrap.sass',
+    ],
+  },
+
+  css: [
+    './assets/sass/style.sass',
+  ],
+}
+

⚠️ 注意
是在 css 匯入 style.sass,而不是在styleResources 匯入。
styleResources 匯入的話,將使得每個 Vue 組件都包含一大包的 Bootstrap 樣式,造成專案容量爆肥,編譯速度也會被嚴重拖慢。

🖖 在 Vue 組件內使用

🎉 大功告成!

如此便可自由自在地在 vue 組件內使用 BS 的 class 和所有 SCSS/SASS 變數。

<template>/* ... */</template>
+<script>/* ... */</script>
+
+<style lang="sass" scoped>
+div
+  @include media-breakpoint-down(sm)
+    background: $primary
+</style>
+
<template>/* ... */</template>
+<script>/* ... */</script>
+
+<style lang="sass" scoped>
+div
+  @include media-breakpoint-down(sm)
+    background: $primary
+</style>
+
+ + + + \ No newline at end of file diff --git a/blog/check-if-key-exists.html b/blog/check-if-key-exists.html new file mode 100644 index 00000000..4bacd41b --- /dev/null +++ b/blog/check-if-key-exists.html @@ -0,0 +1,156 @@ + + + + + + + + + + + + + 檢查物件的 key 是否存在的 N 種方法 | ngseke + +
  • JavaScript

檢查物件的 key 是否存在的 N 種方法

  • Jul 07, 2023
  • by Sean Huang

.hasOwnProperty()

使用物件的 prototype hasOwnProperty 檢查。

不會遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+profile.hasOwnProperty('name') // true
+profile.hasOwnProperty('valueOf') // false
+
const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+profile.hasOwnProperty('name') // true
+profile.hasOwnProperty('valueOf') // false
+

in

最簡潔的寫法,使用關鍵字 in 檢查。

但請留意 in 去尋找整個原型鏈,可能得到預期外的結果,並造成微乎其微的效能差異。

const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+'name' in profile // true
+
+/*
+  ⚠️ 雖然以下 key 並沒有在 `profile` 中明確定義,
+  但因為它們存在於物件的 prototype 中,所以依然會得到 true。
+*/
+'valueOf' in profile // true
+'toString' in profile // true
+'hasOwnProperty' in profile // true
+
const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+'name' in profile // true
+
+/*
+  ⚠️ 雖然以下 key 並沒有在 `profile` 中明確定義,
+  但因為它們存在於物件的 prototype 中,所以依然會得到 true。
+*/
+'valueOf' in profile // true
+'toString' in profile // true
+'hasOwnProperty' in profile // true
+

效能實測:hasOwnProperty 和 in 哪个性能高? - frosen的回答 - 知乎

Object.prototype.hasOwnProperty.call()

由於 JavaScript 未保護 hasOwnProperty,所以你完全可以複寫這個屬性,讓它刻意回傳錯誤的結果。

const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+/* 😢 現在無論傳入什麼都會得到 true */
+profile.hasOwnProperty('🍺') // true
+profile.hasOwnProperty(123) // true
+
const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+/* 😢 現在無論傳入什麼都會得到 true */
+profile.hasOwnProperty('🍺') // true
+profile.hasOwnProperty(123) // true
+

利用 Object.prototype.hasOwnProperty.call() 即可避免此情況發生,也不會遍歷原型鏈。

/* 😎 雖然冗長但最安全 */
+Object.prototype.hasOwnProperty.call(profile, 'name') // true
+Object.prototype.hasOwnProperty.call(profile, '🍺') // false
+
/* 😎 雖然冗長但最安全 */
+Object.prototype.hasOwnProperty.call(profile, 'name') // true
+Object.prototype.hasOwnProperty.call(profile, '🍺') // false
+

Object.hasOwn()

ES13 推出的新特性,旨在取代 Object.prototype.hasOwnProperty(),寫相比之下法更直觀和簡潔。並且和 Object.prototype.hasOwnProperty.call() 一樣,即使複寫了 hasOwnProperty 依然可以得到正確的結果。

不會遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+Object.hasOwn(profile, 'name') // true
+Object.hasOwn(profile, '🍺') // false
+
const profile = {
+  name: 'Sean',
+  age: 24,
+  hasOwnProperty: () => true,
+}
+
+Object.hasOwn(profile, 'name') // true
+Object.hasOwn(profile, '🍺') // false
+

其他常見但有陷阱的方法

Object.keys().includes() 😐

先以 Object.keys() 取得物件的所有 key 的陣列,接著呼叫陣列的方法 includes() 來檢查 key 是否存在。

但這個方法的時間複雜度是 O(n)或更高),因為它必須先至少遍歷物件來得到所有的 key,接著尋找時又得再遍歷一次陣列,在 key 數量一多時顯然很沒效率。

不會遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+const keys = Object.keys(profile) // ['name', 'age']
+
+keys.includes('name') // true
+keys.includes('valueOf') // false
+
const profile = {
+  name: 'Sean',
+  age: 24,
+}
+
+const keys = Object.keys(profile) // ['name', 'age']
+
+keys.includes('name') // true
+keys.includes('valueOf') // false
+

!== undefined 😐

當試圖存取不存在於物件的 key 時,會得到 undefined

但當某 key 存在而且值剛好是 undefined 時,那就仍會得到 false

遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  phone: undefined,
+}
+
+profile.address !== undefined // false
+profile.phone !== undefined // ⚠️ false
+
const profile = {
+  name: 'Sean',
+  phone: undefined,
+}
+
+profile.address !== undefined // false
+profile.phone !== undefined // ⚠️ false
+

!!Boolean() 😕

簡單暴力的寫法,也就是直接將值轉型成 boolean。但這方法顯然很不可靠,因為只要是 falsy 值,例如 0 、空字串 ''null 等 ,即使 key 存在但依然會得到 false

除非你對物件型別有十足的信心,例如在有 TypeScript 的場合,否則不太推薦這寫法。

遍歷原型鏈。

const profile = {
+  name: 'Sean',
+  balance: 0,
+  isDead: false,
+}
+
+!!profile.name // true
+!!profile.balance // ⚠️ false
+Boolean(profile.isDead) // ⚠️ false
+
const profile = {
+  name: 'Sean',
+  balance: 0,
+  isDead: false,
+}
+
+!!profile.name // true
+!!profile.balance // ⚠️ false
+Boolean(profile.isDead) // ⚠️ false
+

參考資料

+ + + + \ No newline at end of file diff --git a/blog/code-review-taxi-number.html b/blog/code-review-taxi-number.html new file mode 100644 index 00000000..381422c7 --- /dev/null +++ b/blog/code-review-taxi-number.html @@ -0,0 +1,171 @@ + + + + + + + + + + + + + 如果太認真去 Code Review 會發生什麼事? | ngseke + +
  • JavaScript

如果太認真去 Code Review 會發生什麼事?

  • May 31, 2023
  • by Sean Huang

前言

Code Review 實在是太重要了,我想沒人會否認。

在軟體開發的世界中,需求不斷地變化是家常便飯,程式碼的可讀性和可維護性顯得格外重要。然而 Code Review 要看的多深、多仔細、花多少時間看,想找到適當的平衡點,其中分寸的拿捏確實是個挑戰。

但我始終認為有總比沒有好,畢竟 Code Review 是維持程式碼品質最有效的方式。相信大部分開發者都有同感: 「不是新需求難實現,而是舊的扣改不動。」 有多少人曾被困在遺留代碼之中,為了加一個新功能,首先得耗費無數的精力,試圖理解那些又長又冗、變數胡亂命名的程式碼。接著才發現函式和函式之間耦合的嚴重,想要重構又因為副作用太多而不敢輕舉妄動。

因此在這篇文章中我想模擬這樣一個情境:

如果很嚴格地進行 Code Review,最吹毛求疵的那種,那看起來會像什麼樣子?

我將以各種壞味道的角度,分析一段出現在真實場景的程式碼,並提供修改建議。同時也分享可套用的對應 ESLint 規則,其官方文件也會解釋這條規則存在的理由是什麼。

Review 原則

  1. 程式碼應具可讀性
  2. 以盡量少的程式碼實現相同的功能
  3. 避免潛在副作用和壞味道

需求描述

  1. 函式接受任意字串,轉換成用來表示「計程車的數量」的數字字串,介於 030
  2. 小於 0 時應總是得到 0;大於 30 時總是應得到 30
  3. 無法用 parseInt 解析的字串一律轉換成 0
  4. 轉換後的字串不足兩位數時,左邊補上 0

原程式碼片段

這 13 行代碼包括註解一字不差地擷自真實專案,其中的人名已替換成假名

// 2021-11-23 Sean : 轉型 > 補0
+const TaxiNumber = (v) => {
+  // 正規表示
+  let regex = new RegExp(/^\d{1,3}$/);
+  // 叫車最小值、最大值
+  let minTaxi = 0, maxTaxi = 30;
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  v = parseInt(v);
+  // 字串判斷?數值判斷
+  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+  // 回傳 & 補0
+  return v.toString().padStart(2, '0');
+}
+
// 2021-11-23 Sean : 轉型 > 補0
+const TaxiNumber = (v) => {
+  // 正規表示
+  let regex = new RegExp(/^\d{1,3}$/);
+  // 叫車最小值、最大值
+  let minTaxi = 0, maxTaxi = 30;
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  v = parseInt(v);
+  // 字串判斷?數值判斷
+  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+  // 回傳 & 補0
+  return v.toString().padStart(2, '0');
+}
+

壞味道一:意圖不明的函式命名

const TaxiNumber = (v) => {
+      ^^^^^^^^^^
+
const TaxiNumber = (v) => {
+      ^^^^^^^^^^
+

我首先注意到 TaxiNumber 這個單詞是大駝峰,當讀者單獨看時很容易產生困惑:它是用來展示計程車數字的 React Component 嗎?又或是 class?但都不是,它只是用來轉換字串的函式。

函式通常是以動詞開頭,而且應命名得清晰明瞭,能夠表達其功能或操作目的。

這類函式建議以 convertparseformatnormalize 等動詞開頭,例如 convertAToB,且按照 JavaScript 慣例通常會以小駝峰命名。

💡 套用 ESLint 規則 camelcase 可約束一致變數的命名風格。

壞味道二:縮寫的參數

const TaxiNumber = (v) => {
+                    ^
+
const TaxiNumber = (v) => {
+                    ^
+

v 可能是 value 的縮寫,隨個人喜好略縮單詞是個常見的壞味道,通常只有作者本人才知道其背後的含義。但要知道身為團隊的一員,自己不會是唯一維護這段程式碼的人。命名前多花個幾秒仔細思考,將心比心地為下一個接手的人,甚至是一個月後的自己著想,確保它清晰易懂,任何人看了都能夠馬上理解。

建議替換成 countString ,或是任意一具描述性單詞。

💡 套用 ESLint 規則 id-denylist 可以定義「禁詞」,禁止使用這些單詞來命名,例如單字母 a b c,或是毫無意義的 datavalue

壞味道三:冗余的建構式

  let regex = new RegExp(/^\d{1,3}$/);
+              ^^^^^^^^^^
+
  let regex = new RegExp(/^\d{1,3}$/);
+              ^^^^^^^^^^
+

在這個情境並沒打算以 template literals 來動態產生 pattern,例如 new RegExp(`^{${a}} + + + + + + + + + + + + + + + + ),所以可直接省略掉 new RegExp(),直接寫成更簡短的 /^\d{1,3}$/ 即可。

💡 套用 ESLint 規則 prefer-regex-literals 來偏好採用 regular expression literals 寫法。

當能用更簡潔的寫法達成一模一樣的效果時,就沒理由選擇冗長的那個。其他類似的冗余的建構式還有這些:

  • {} ,而非 new Object()
  • [] ,而非 new Array()

💡 套用 ESLint 規則 no-new-objectno-array-constructor 來偏好採用 literal notation 寫法。

壞味道四:從未改變的 let

  let minTaxi = 0, maxTaxi = 30;
+  ^^^
+
  let minTaxi = 0, maxTaxi = 30;
+  ^^^
+

一個簡單的原則是永遠優先使用 const 來宣告變數,除非真的有需要重新賦值。因此建議改寫成:

/* 常見風格 */
+const min = 0, max = 30
+
+/* 陣列解構風格 */
+const [min, max] = [0, 30]
+
/* 常見風格 */
+const min = 0, max = 30
+
+/* 陣列解構風格 */
+const [min, max] = [0, 30]
+

順帶一提, [number, number] 的寫法在 TypeScript 中稱作 tuple,在這隱含一種成雙成對的概念,有助於讀者把它們理解成是「一組範圍」的數字。

💡 套用 ESLint 規則 prefer-const 檢查從未被重新賦值的變數。支援 auto fix,可以自動將 let 替換成 const

壞味道五:對參數重新賦值

  v = parseInt(v);
+    ^
+
  v = parseInt(v);
+    ^
+

無論任何語言,複寫函式的參數從來不是個好主意,可參考一些針對 reassign parameter 的討論。因為這很容易誤導讀者,原本預期傳入的參數是 string,但突然在某一行後卻硬生生變成了 number。

若專案有導入 TypeScript,某種程度也可以避免出現這種寫法,例如在此例中,因為新值顯然不符合參數原本的型別,compiler 便會直接報錯。

💡 套用 ESLint 規則 no-param-reassign 來防止參數被 reassign。

壞味道六:冗長的三元運算子

  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
  v = regex.test(v.toString()) ? v < minTaxi ? minTaxi : v > maxTaxi ? maxTaxi : v : minTaxi;
+                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+

巢狀的三元運算子,多個 :? 交錯,沒有換行與縮排,缺乏可讀性。

建議採用具有更明確的語意的 Math.min()Math.max() 方法改寫,並加上適當的換行和縮排。

  v = regex.test(v.toString())
+    ? Math.min(Math.max(v, minTaxi), maxTaxi)
+    : minTaxi
+
  v = regex.test(v.toString())
+    ? Math.min(Math.max(v, minTaxi), maxTaxi)
+    : minTaxi
+

💡 套用 ESLint 規則 no-nested-ternary 來禁止巢狀的三元運算子。

壞味道七:深入變數的命名藝術

TaxiNumber

number 一詞作為變數有點太籠統了,也沒有好好地起到描述的作用,說到底任何只要是數值想必都會是 number。例如寬度、角度和價格它們都是 number。

要描述這類變數,相信一定找得到比 number 還更貼切的單詞。

根據需求它是用來表示「計程車的數量」,對於這類計數/累加的變數,建議可命名成 count

minTaxi

第一眼見到 minTaximaxTaxi ,有點難想像其背後的含義是什麼。讀者可能會把他誤解成車型的大小,或是計程車的載客數。

在這個情況建議單純命名成 min 就行,把 taxi 當作後綴反而不適合,因為實際上要描述的是計程車的「數量」而非「計程車本身」。若想更精準的描述,可以考慮用 minCountminTaxiCount

壞味道八:「// 到此一遊

// 2021-11-23 Sean : 轉型 > 補0
+
// 2021-11-23 Sean : 轉型 > 補0
+

在未導入版控的遺留專案中,常見在註解裡出現作者的大名。但在現代軟體開發早以 git 進行版控,透過 git blame 就能追溯每一行的修改者是誰、何時修改的、以及對應的 commit message 等。

  // 正規表示
+  // 叫車最小值、最大值
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  // 字串判斷?數值判斷
+  // 回傳 & 補0
+
  // 正規表示
+  // 叫車最小值、最大值
+  // 轉型,利用轉型與輸入特性,處裡數字後字串
+  // 字串判斷?數值判斷
+  // 回傳 & 補0
+

事實上,不只第一行的註解,這整段所有的註解可能都是多餘的,因為程式碼做的事太顯而易見了,註解反而變成閱讀時的雜訊。善用能夠自我描述的變數和函式命名是最好的方法,因為它們本身就能夠清楚地傳達代碼的意圖,不再需要額外的註解。

code comment be like

最終重構結果

經過分析不難發現這個函式沒必要用上 RegExp,單純用 parseInt() 就足夠了。因為當傳入不合法的字串時,它就會一律回傳 NaN,也不會拋出任何錯誤。KISS(Keep It Simple, Stupid)原則便很好地說明了這個道理,複雜的設計往往容易出錯,增加不必要的複雜度和 debug 的難度,因此應傾向以簡單和直接的方式解決問題,以保持程式碼的清晰度和可讀性。

綜合上述幾點,經過 TypeScript 重構後的程式碼可以長得像這樣:

const parseTaxiCount = (countString: string) => {
+  const [min, max] = [0, 30]
+  const count = parseInt(countString) || 0
+  const clampedCount = Math.min(Math.max(count, min), max)
+  const paddedCount = String(clampedCount).padStart(2, '0')
+
+  return paddedCount
+}
+
const parseTaxiCount = (countString: string) => {
+  const [min, max] = [0, 30]
+  const count = parseInt(countString) || 0
+  const clampedCount = Math.min(Math.max(count, min), max)
+  const paddedCount = String(clampedCount).padStart(2, '0')
+
+  return paddedCount
+}
+

最後也附上單元測試(Jest):

it('parseTaxiCount()', () => {
+  expect(parseTaxiCount('')).toBe('00')
+  expect(parseTaxiCount(' ')).toBe('00')
+  expect(parseTaxiCount('-1')).toBe('00')
+  expect(parseTaxiCount('0')).toBe('00')
+  expect(parseTaxiCount('1')).toBe('01')
+  expect(parseTaxiCount('01')).toBe('01')
+  expect(parseTaxiCount('15')).toBe('15')
+  expect(parseTaxiCount('30')).toBe('30')
+  expect(parseTaxiCount('31')).toBe('30')
+  expect(parseTaxiCount('999')).toBe('30')
+  expect(parseTaxiCount('abc')).toBe('00')
+  expect(parseTaxiCount('1a1')).toBe('01')
+  expect(parseTaxiCount('30aaa')).toBe('30')
+  expect(parseTaxiCount('030aaa')).toBe('30')
+})
+
it('parseTaxiCount()', () => {
+  expect(parseTaxiCount('')).toBe('00')
+  expect(parseTaxiCount(' ')).toBe('00')
+  expect(parseTaxiCount('-1')).toBe('00')
+  expect(parseTaxiCount('0')).toBe('00')
+  expect(parseTaxiCount('1')).toBe('01')
+  expect(parseTaxiCount('01')).toBe('01')
+  expect(parseTaxiCount('15')).toBe('15')
+  expect(parseTaxiCount('30')).toBe('30')
+  expect(parseTaxiCount('31')).toBe('30')
+  expect(parseTaxiCount('999')).toBe('30')
+  expect(parseTaxiCount('abc')).toBe('00')
+  expect(parseTaxiCount('1a1')).toBe('01')
+  expect(parseTaxiCount('30aaa')).toBe('30')
+  expect(parseTaxiCount('030aaa')).toBe('30')
+})
+

結語

以上分析基於個人開發習慣,提供一些在 Code Review 時可作為參考的觀點,其中很多屬於很主觀的意見,未必是最佳解。因此每個團隊都應該對程式碼風格有所共識,你會發現其實絕大部分都可以交給 ESLint 等自動化工具檢查,將寶貴的 Code Review 時間花在確認是否符合需求、架構的設計是否合理、評估演算法的效能和擴展性等。

Code Review 的目的不僅僅是確保程式碼的品質和一致性,更重要的是促進團隊之間的合作和溝通。這是一個讓開發者互相學習、分享最佳實踐和技巧,以及達成共識的過程。因此在這個過程中,要盡可能保持開放和建設性的討論,避免淪為單純的批評和指責。

+ + + + \ No newline at end of file diff --git a/blog/component-naming.html b/blog/component-naming.html new file mode 100644 index 00000000..f631541e --- /dev/null +++ b/blog/component-naming.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + + 試著用有點違反直覺的方式命名組件 | ngseke + +
  • Vue
  • React

試著用有點違反直覺的方式命名組件

  • Nov 28, 2021
  • by Sean Huang

前言:30 秒越南語小教室 🇻🇳

越南語是形容詞後置的語言,恰恰和華語或英語相反。

例如「越南語」這個詞會寫成 「語越南」

Tiếng Việt Nam
+ ()  ( 越南 )
+
Tiếng Việt Nam
+ ()  ( 越南 )
+

「台灣人」則會寫做 「人台灣」

Người Đài Loan
+ ()  ( 台灣 )
+
Người Đài Loan
+ ()  ( 台灣 )
+

修飾語擺後面

在 Vue 官方風格指南中,組件命名的概念與越南語文法不謀而合。

若按照英語的文法,人們通常習慣這樣命名組件:

  • Primary (a.) + Button (n.) = PrimaryButton
  • Date (n.) + TextField (n.) = DateTextField
  • Confirm (v.) + Dialog (n.) = ConfirmDialog

但如指南所述,它認為應該把🔵主語/主詞(文中所謂高級別的詞)擺到前面;🔴修飾性的詞則是放在結尾。形容詞、動詞或名詞都可算是修飾詞。

因此推薦的命名法如下:

  • Button (n.) + Primary (a.) = ButtonPrimary
  • TextField (n.) + Date (n.) = TextFieldDate
  • Dialog (n.) + Confirm (v.) = DialogConfirm

在特定語境內的組件

對於在🟢特定語境內才有意義的的組件──也就是在某些個別頁面,或是只有在特定組件中用得到的子組件,指南中也建議以父組件或頁面名當作前綴

例如:

  • Search (父組件) + TextField + Date = SearchTextFieldDate
    Search 組件內專用的日期輸入框
  • Search (父組件) + Button + Submit = SearchButtonSubmit
    Search 組件內專用的送出按鈕
  • LandingPage (頁面) + List + News = LandingPageListNews
    只在 Landing Page 用到的新聞列表
  • LandingPage (頁面) + List + Features = LandingPageListFeatures
    只在 Landing Page 用到的產品特色列表

停止巢狀資料夾!

你可能試過以一層又一層的目錄來區分組件,像是:

components/
+  |- LandingPage/
+     |- News/
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+     |- Features.tsx
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+
components/
+  |- LandingPage/
+     |- News/
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+     |- Features.tsx
+        |- Item.tsx
+        |- List.tsx
+        |- Title.tsx
+

但你其實可以放心、大膽地將它們都放在同一層資料夾。

重新調整後的目錄結構如下:

components/
+  |- LandingPageNewsItem.tsx
+  |- LandingPageNewsList.tsx
+  |- LandingPageNewsTitle.tsx
+  |- LandingPageFeaturesItem.tsx
+  |- LandingPageFeaturesList.tsx
+  |- LandingPageFeaturesTitle.tsx
+
components/
+  |- LandingPageNewsItem.tsx
+  |- LandingPageNewsList.tsx
+  |- LandingPageNewsTitle.tsx
+  |- LandingPageFeaturesItem.tsx
+  |- LandingPageFeaturesList.tsx
+  |- LandingPageFeaturesTitle.tsx
+

But Why?

檔案總管的排序

絕大多數 IDE 例如 VSCode,它們的檔案總管都是按照英文字母排序的,所以前綴相同的那些文件,都會集中在一塊兒,從命名一眼就能看出哪些組件具有較高關聯性。

「滾動」比「展開資料夾」更無負擔

若要從茫茫組件庫的大海中找尋某個組件,一個一個點擊並展開深層的巢狀資料夾是非常惱人的。更別提有些打開、有些關閉的資料夾相互交錯混雜的畫面,看了更是令人煩躁不安。

相比之下,用滑鼠滾動在同一層目錄瀏覽,顯然更有效率且輕鬆許多。

移動組件的困難度

對於尚未導入 TypeScript 的專案,想移動組件可能是件大工程。因為 VSCode 可能無法理解組件之間的相依關係,而無法順利地自動進行重構。

假設有一個組件 components/searchBar/Input/DateIuput.tsx,今天我想將它提升至上層的 components/Input/ 目錄,讓其他更多組件共用。然而我別無選擇,只能在專案中進行的全域搜尋並取代路徑字串,同時也得背負意外替換到其他無辜字串的風險。

撞名組件在視覺上難以定位

利用 ⌘ + P 搜尋眾多同名的組件時,在 VSCode 裡很難快速定位到目標,因為它們乍看之下都一模一樣。

同樣地,出現在頁籤中的多個同名檔案也是讓人眼花撩亂。

結語

本文中所提到的命名方式並非強制性的,它只是一種風格,在實務上還是應該考量專案的規模,根據團隊固有的習慣來權衡。不過如果今天你要做的是個人的 side project(例如本專案),那我會非常推薦你不如大膽地嘗試看看。

參考資料

+ + + + \ No newline at end of file diff --git a/blog/customize-youtube-caption-font.html b/blog/customize-youtube-caption-font.html new file mode 100644 index 00000000..9565ee9a --- /dev/null +++ b/blog/customize-youtube-caption-font.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + 把 YouTube CC 字幕變成粉圓體 | ngseke + +
  • Tampermonkey

把 YouTube CC 字幕變成粉圓體

  • Aug 14, 2022
  • by Sean Huang

腳本連結

https://gist.github.com/ngseke/8b30050e05703f35c753cd0ac6330028

說明

YouTube CC 字幕雖然有內建設定字體的功能,然而可用的選項並不多,這些選項也都是基於英文字體。

如果你像我一樣對圓體有莫名的愛好,那你一定不能錯過這個腳本。它可以將 YouTube 的字幕替換成任何你想要的字體,前提是你的電腦要先安裝好該字體。

const fontFamily = '"jf-openhuninn-1.1"'
+//                   ^^^^^^^^^^^^^^^^^
+
const fontFamily = '"jf-openhuninn-1.1"'
+//                   ^^^^^^^^^^^^^^^^^
+

將第 :13 行替換成想要的字體名稱即可,例如本例用的是開源圓體「jf open 粉圓」,注意必須保留裡面的雙引號。

結果

Before:

After:

+ + + + \ No newline at end of file diff --git a/blog/react-handler-type.html b/blog/react-handler-type.html new file mode 100644 index 00000000..6ed94686 --- /dev/null +++ b/blog/react-handler-type.html @@ -0,0 +1,220 @@ + + + + + + + + + + + + + 處理在 React 抽出 event handler 時常碰到的 TypeScript 參數型別問題 | ngseke + +
  • React
  • TypeScript

處理在 React 抽出 event handler 時常碰到的 TypeScript 參數型別問題

  • Jun 06, 2022
  • by Sean Huang

TL;DR

如果某個 event handler 的事件參數,就只是單純拿來呼叫 preventDefault()stopPropagation(),那麼無論它是什麼元素,只要將 event 指定成 SyntheticEvent 型別即可,寫起來也非常簡潔。

import { SyntheticEvent } from 'react' // 記得 import
+
+// ...
+
+const handleSubmit = (event: SyntheticEvent) => {
+  event.preventDefault()
+}
+
+return (
+  <form onSubmit={handleSubmit}>
+    {/* ... */}
+  </form>
+)
+
import { SyntheticEvent } from 'react' // 記得 import
+
+// ...
+
+const handleSubmit = (event: SyntheticEvent) => {
+  event.preventDefault()
+}
+
+return (
+  <form onSubmit={handleSubmit}>
+    {/* ... */}
+  </form>
+)
+

分析

考慮以下處理表單送出的程式碼:

function App () {
+  return (
+    <form
+      onSubmit={(event) => {
+        event.preventDefault()
+        /* ... */
+      }}
+    >
+      {/* ... */}
+    </form>
+  )
+}
+
function App () {
+  return (
+    <form
+      onSubmit={(event) => {
+        event.preventDefault()
+        /* ... */
+      }}
+    >
+      {/* ... */}
+    </form>
+  )
+}
+

有時候會想把 inline event handler onSubmit 給抽出來,變成 handleSubmit

但直接取出函式當然是行不通的,因為 TypeScript 無從推斷出參數 event 的型別。

function App () {
+  const handleSubmit = (event) => {
+    //                  ^^^^^
+    // Parameter 'event' implicitly has an 'any' type.ts(7006)
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+
function App () {
+  const handleSubmit = (event) => {
+    //                  ^^^^^
+    // Parameter 'event' implicitly has an 'any' type.ts(7006)
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+

在某些情況,如果 handler 裡根本沒用到 event 這個參數,例如常用的 onClick 事件,那麼這時其實把參數直接移除就行。

const handleClick = () => {
+  console.log('Clicked')
+}
+
+return (
+  <button onClick={handleClick}>
+    Button
+  </button>
+)
+
const handleClick = () => {
+  console.log('Clicked')
+}
+
+return (
+  <button onClick={handleClick}>
+    Button
+  </button>
+)
+

然而在處理表單的 onSubmit 的場合時,經常會需要呼叫這個事件的 preventDefault(),用來阻止事件傳遞。

因此我們常常得 「復刻」 出這個事件的型別,而不得不寫出冗長難讀的型別,例如 React.FormEventHandler<HTMLFormElement>

function App () {
+  const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+
function App () {
+  const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
+    event.preventDefault()
+  }
+
+  return (
+    <form onSubmit={handleSubmit}>
+      {/* ... */}
+    </form>
+  )
+}
+

BTW 這一長串型別的定義,可以透過將游標放在 JSX 事件上得知。

Synthetic Event

翻開 React 的官方文件可以得知,在 React 中的所有事件都是 Synthetic Event,而原生的 Event。兩者的 API 雖然長得很相似,但實際上並不是同一個東西。

可以試著追溯 onSubmit 事件的型別來觀察這件事:

// node_modules/@types/react/index.d.ts:1383
+interface DOMAttributes<T> {
+  // ...
+  onSubmit?: FormEventHandler<T> | undefined;
+}
+
// node_modules/@types/react/index.d.ts:1383
+interface DOMAttributes<T> {
+  // ...
+  onSubmit?: FormEventHandler<T> | undefined;
+}
+

再往上查看 FormEventHandlerFormEvent

// node_modules/@types/react/index.d.ts:1303
+type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
+
+// node_modules/@types/react/index.d.ts:1195
+interface FormEvent<T = Element> extends SyntheticEvent<T> {
+}
+
// node_modules/@types/react/index.d.ts:1303
+type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
+
+// node_modules/@types/react/index.d.ts:1195
+interface FormEvent<T = Element> extends SyntheticEvent<T> {
+}
+

由此可知 onSubmit 確實是一層層地從 SyntheticEvent extend 出來的。

可以再進一步查看 SyntheticEvent 型別的定義,即可看到 preventDefault() 方法:

interface SyntheticEvent<T = Element, E = Event>
+  extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
+
+interface BaseSyntheticEvent<E = object, C = any, T = any> {
+  nativeEvent: E;
+  currentTarget: C;
+  target: T;
+  bubbles: boolean;
+  cancelable: boolean;
+  defaultPrevented: boolean;
+  eventPhase: number;
+  isTrusted: boolean;
+  preventDefault(): void;
+  isDefaultPrevented(): boolean;
+  stopPropagation(): void;
+  isPropagationStopped(): boolean;
+  persist(): void;
+  timeStamp: number;
+  type: string;
+}
+
interface SyntheticEvent<T = Element, E = Event>
+  extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
+
+interface BaseSyntheticEvent<E = object, C = any, T = any> {
+  nativeEvent: E;
+  currentTarget: C;
+  target: T;
+  bubbles: boolean;
+  cancelable: boolean;
+  defaultPrevented: boolean;
+  eventPhase: number;
+  isTrusted: boolean;
+  preventDefault(): void;
+  isDefaultPrevented(): boolean;
+  stopPropagation(): void;
+  isPropagationStopped(): boolean;
+  persist(): void;
+  timeStamp: number;
+  type: string;
+}
+

結論

經過分析可以得知,其實以下任一種寫法都可以使 handleSubmit 相容 onSubmit 事件。

const handleSubmit = (e: React.SyntheticEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit = (e: React.SyntheticEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit = (e: React.FormEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit = (e: React.FormEvent) => {
+  e.preventDefault()
+}
+
const handleSubmit: React.FormEventHandler = (e) => {
+  e.preventDefault()
+}
+
const handleSubmit: React.FormEventHandler = (e) => {
+  e.preventDefault()
+}
+
+ + + + \ No newline at end of file diff --git a/blog/reproduce-bootstrap-grid-in-tailwind.html b/blog/reproduce-bootstrap-grid-in-tailwind.html new file mode 100644 index 00000000..ca702a11 --- /dev/null +++ b/blog/reproduce-bootstrap-grid-in-tailwind.html @@ -0,0 +1,68 @@ + + + + + + + + + + + + + 用 Tailwind 重現 Bootstrap 的 Grid System | ngseke + +
  • Tailwind
  • Bootstrap

用 Tailwind 重現 Bootstrap 的 Grid System

  • Aug 22, 2022
  • by Sean Huang

此筆記提到的 Grid 指的是元件庫 Bootstrap 所提供的的網格系統(Grid system),和 CSS 的 display: grid 屬性無關。

轉換後的 class 屬性有稍微簡化,但我想足以覆蓋大部分的使用場景了。

Bootstrap 和 Tailwind 的 Class Name 對應

row

flex flex-wrap

col

flex-1

備註:

.flex-1
+  flex: 1 // 即 `1 1 0%`
+
.flex-1
+  flex: 1 // 即 `1 1 0%`
+

col-auto

flex-none

備註:

.flex-none
+  flex: none // 即 `0 0 auto`
+
.flex-none
+  flex: none // 即 `0 0 auto`
+

col-*

  • col-1flex-none w-1/12
  • col-3flex-none w-1/4
  • col-4flex-none w-1/3
  • col-6flex-none w-1/2
  • col-12flex-none w-full

有需要的話,你甚至可以在 Tailwind 透過 Arbitrary Value 的特性來指定任意寬度,例如:
w-[5rem]w-[50px]

w-100

w-full

用於強迫換行。

響應式(RWD)

By default, Tailwind uses a mobile first breakpoint system, …
Breakpoints · Bootstrap v5.2

Mobile first, responsive design is the goal. …
Responsive Design - Tailwind CSS

Tailwind 和 Bootstrap 都同樣遵循著行動裝置優先的設計哲學,寬度由小到大。也就是說,當什麼斷點都沒加的時候,樣式會套用到所有寬度的裝置(>= 0px),而加上 sm 時會套用在 >= 640px,再加上 md 時會套用在 >= 768px,依此類推。

值得注意的是,它們兩者斷點的預設值稍微不太一樣,例如 sm 在 Tailwind 是 640px,在 Bootstrap 則是 576px。不過這完全不成問題,因為你可以在 tailwind.config.js 輕鬆的自定義,甚至自創斷點名稱。

轉換範例如下:

  • col-12flex-none w-full
  • col-md-6flex-none md:w-1/2
  • col-3 col-sm-auto col-md col-lg-12
    flex-1 w-1/4 sm:flex-none sm:w-auto md:flex-1 lg:flex-none lg:w-full

間距(Gutter)

在預設情況下,column 之間是沒有間距的。這相當於是套用了 Bootstrap 4 的 row no-gutters,或是 Bootstrap 5 的 row g-0

在 Tailwind 的場合想要實現 gutter,必須另外做以下兩點:

  1. 為 row 元素加上負值的水平 margin,例如 -mx-4
  2. 為每一個 column 元素,加上與 row 元素相對應的水平 padding,例如 px-4

row 的負值 margin 是用來抵銷在最左或最右邊緣的 column 的 padding。

範例

col-12
col-6
col-3
col
col-auto
<div class="-mx-4 flex flex-wrap">
+  <div class="w-full flex-none px-4">
+    col-12
+  </div>
+  <div class="w-1/2 flex-none px-4">
+    col-6
+  </div>
+  <div class="w-1/4 flex-none px-4">
+    col-3
+  </div>
+  <div class="w-full"><!-- 換行用 --></div>
+  <div class="flex-none px-4">
+    col
+  </div>
+  <div class="flex-1 px-4">
+    col-auto
+  </div>
+</div>
+
<div class="-mx-4 flex flex-wrap">
+  <div class="w-full flex-none px-4">
+    col-12
+  </div>
+  <div class="w-1/2 flex-none px-4">
+    col-6
+  </div>
+  <div class="w-1/4 flex-none px-4">
+    col-3
+  </div>
+  <div class="w-full"><!-- 換行用 --></div>
+  <div class="flex-none px-4">
+    col
+  </div>
+  <div class="flex-1 px-4">
+    col-auto
+  </div>
+</div>
+

延伸閱讀

flex-basiswidth 的差別是什麼?

+ + + + \ No newline at end of file diff --git a/blog/typescript-as-const.html b/blog/typescript-as-const.html new file mode 100644 index 00000000..cb0c0d73 --- /dev/null +++ b/blog/typescript-as-const.html @@ -0,0 +1,136 @@ + + + + + + + + + + + + + 讓 TypeScript 的 as const 救你一命 | ngseke + +
  • TypeScript

讓 TypeScript 的 as const 救你一命

  • Dec 03, 2021
  • by Sean Huang

這篇文章討論的是 TypeScript 特有的「常數斷言(Const Assertion)」語法,
和 JavaScript 宣告常數所使用的 const foo = 1 是不同東西。

情境一:常數字串陣列

情境描述

🎡 TS Playground

想像我正在建立一個切換難易度的功能,有三種難度可選,先將他們宣告成陣列。

const options = ['easy', 'normal', 'hard']
+
const options = ['easy', 'normal', 'hard']
+

接著再建立一個變數 difficulty,用來記錄目前選中的難易度。

type Difficulty = typeof options[number] // string
+
+let difficulty: Difficulty = options[0]
+
type Difficulty = typeof options[number] // string
+
+let difficulty: Difficulty = options[0]
+

但仔細一看型別 Difficulty 居然是 string,這也太隨便了,因為這代表我可以把 difficulty 設成隨便一個字串,TypeScript 也不會阻止我。

difficulty = '安安' // 😑
+
difficulty = '安安' // 😑
+

解法

🎡 TS Playground

這時 as const (const assertion)就派上用場了!寫法就像型別斷言那樣。

const options = ['easy', 'normal', 'hard'] as const
+
+// 或是使用尖括弧(tsx 以外的檔案才能用這種寫法)
+const options = <const>['easy', 'normal', 'hard']
+
const options = ['easy', 'normal', 'hard'] as const
+
+// 或是使用尖括弧(tsx 以外的檔案才能用這種寫法)
+const options = <const>['easy', 'normal', 'hard']
+

如此即可得到預期中準確的型別。

type Difficulty = typeof difficulties[number] // "easy" | "normal" | "hard"
+
type Difficulty = typeof difficulties[number] // "easy" | "normal" | "hard"
+

搭配 VSCode 的 IntelliSense 可以得到代碼提示,也不再怕手殘拼錯字。(快速鍵 ⌘ + I 呼出)

map() 等回調函式的參數也會自動推斷出型別。

// 假設有另一個函式參數要求傳入 `Difficulty`
+const foo = (difficulty: Difficulty) => { /* ... */ }
+
+// difficulty: "easy" | "normal" | "hard"
+options.map(difficulty => foo(difficulty))
+
// 假設有另一個函式參數要求傳入 `Difficulty`
+const foo = (difficulty: Difficulty) => { /* ... */ }
+
+// difficulty: "easy" | "normal" | "hard"
+options.map(difficulty => foo(difficulty))
+

當然你也可以一開始就先把型別定義好,再以型別約束變數。
但我個人通常不太這麼做,因為這樣會重複出現很雷同的代碼,相當於需要費力維護多個真相來源。例如:

type Difficulty = 'easy' | 'normal' | 'hard'
+const options: Difficulty[] = ['easy', 'normal', 'hard']
+
type Difficulty = 'easy' | 'normal' | 'hard'
+const options: Difficulty[] = ['easy', 'normal', 'hard']
+

不過把型別抽出來單獨定義,還是有額外好處的。假設因為需求改變,需要把 'easy' 改名成 'simple',就可以活用 VSCode 的 重新命名符號(Rename Symbol) 功能來快速重構(從右鍵選單或快速鍵 F2 呼出)。

而這便是 as const 無法做到的,因此建議還是根據實際使用場景做權衡。

情境二:日期區間的 tuple

情境描述

🎡 TS Playground

想像我正在建立一個從開始到結束的時間區間變數 range,一樣宣告成陣列。

const range = [new Date(), new Date()]
+
const range = [new Date(), new Date()]
+

還有一個參數是時間區間的函式。

function foo (range: [Date, Date]) { /* ... */ }
+
function foo (range: [Date, Date]) { /* ... */ }
+

接著試著把 range 放入函式 foo() 中,但卻出現型別不相容的錯誤。

foo(range)
+// Argument of type 'Date[]' is not assignable to parameter of type '[Date, Date]'.
+//   Target requires 2 element(s) but source may have fewer.(2345)
+
foo(range)
+// Argument of type 'Date[]' is not assignable to parameter of type '[Date, Date]'.
+//   Target requires 2 element(s) but source may have fewer.(2345)
+

分析跳出的錯誤得知:
Date[] 不可指派給 [Date, Date],因為函式只接受長度剛好是 2 的陣列,但 range 卻可能是任何長度的陣列。

TypeScript 說的確實沒錯,即使原本 range 是用 const 宣告,但我在中途還是有機會偷改他。例如 push 新的東西進去,或是執行 range.length = 0 來清空陣列,這些都會改變陣列的長度。

解法

🎡 TS Playground

試著加上 as const

const range = [new Date(), new Date()] as const
+
const range = [new Date(), new Date()] as const
+

檢查 range 的型別,會發現它從原本的 Date[] 變成 readonly [Date, Date] 了。

type MyRange = typeof range // readonly [23, 28]
+
type MyRange = typeof range // readonly [23, 28]
+

此後便無法再對 range 進行任何會改變它的操作了。

range.length = 0 // 🚫
+range.push(new Date()) // 🚫
+range.reverse() // 🚫
+
range.length = 0 // 🚫
+range.push(new Date()) // 🚫
+range.reverse() // 🚫
+

接著即可成功將 range 傳入函式。

const range = [new Date(), new Date()] as const
+
+function foo (range: readonly [Date, Date]) { /* ... */ }
+
+foo(range) // ✅
+
const range = [new Date(), new Date()] as const
+
+function foo (range: readonly [Date, Date]) { /* ... */ }
+
+foo(range) // ✅
+

留意函式的參數型別多加上了 readonly 關鍵字,那是用來告訴 TypeScript,這個函式不會去動到 range,例如執行 range[0] = ... 來重新賦值陣列的某個項目。

而試圖存取超出陣列範圍的項目時,TypeScript 也會提示錯誤。(JavaScript 的情況則是會無聲地得到 undefined

// ❌ Tuple type '[Date, Date]' of length '2' has no element at index '100'.(2493)
+range[100]
+
// ❌ Tuple type '[Date, Date]' of length '2' has no element at index '100'.(2493)
+range[100]
+

甚至可以很明確地得知參數 length 的型別為 2

type Length = typeof range.length // 2
+
type Length = typeof range.length // 2
+

總結

as const 套用在不同型別的變數上會得到不同的效果:

string、number、boolean

字面型別(literal type)加上 as const 後,型別就不會被「拓寬」,例如字串 'hello' 不會被推斷成 string,而是會維持原樣。

let a = 'hello' // string
+let b = 'hello' as const // 'hello'
+
+let c = 123 // number
+let d = 123 as const // 123
+
+let e = true // boolean
+let f = true as const // true
+
let a = 'hello' // string
+let b = 'hello' as const // 'hello'
+
+let c = 123 // number
+let d = 123 as const // 123
+
+let e = true // boolean
+let f = true as const // true
+

陣列

陣列會被轉換成 readonly 的 tuple,也就是:

  1. 唯獨,陣列裡面的值始終相同,也不能被修改
  2. 長度永遠固定,不能執行 push()pop() 等操作
let a = [123, 'hello'] // (string | number)[]
+let b = [123, 'hello'] as const // readonly [123, 'hello']
+
let a = [123, 'hello'] // (string | number)[]
+let b = [123, 'hello'] as const // readonly [123, 'hello']
+

物件

物件裡的所有屬性都會被加上 readonly,並且裡面的 string、number、boolean 和陣列值都會比照上述處理,型別不會被拓寬。

let a = { text: 'hello', nested: { count: 123 } }
+// {
+//   text: string
+//   nested: { count: number }
+// }
+
+let b = { text: 'hello', nested: { count: 123 } } as const
+// {
+//   readonly text: "hello"
+//   readonly nested: { readonly count: 123; }
+// }
+
let a = { text: 'hello', nested: { count: 123 } }
+// {
+//   text: string
+//   nested: { count: number }
+// }
+
+let b = { text: 'hello', nested: { count: 123 } } as const
+// {
+//   readonly text: "hello"
+//   readonly nested: { readonly count: 123; }
+// }
+

參考資料

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions

+ + + + \ No newline at end of file diff --git a/blog/vite-vue-ts-eslint-setup.html b/blog/vite-vue-ts-eslint-setup.html new file mode 100644 index 00000000..ca9de367 --- /dev/null +++ b/blog/vite-vue-ts-eslint-setup.html @@ -0,0 +1,188 @@ + + + + + + + + + + + + + 建立 Vite + Vue + TypeScript + ESLint 專案可能會遇到的坑 | ngseke + +
  • Vite
  • Vue
  • TypeScript
  • ESLint

建立 Vite + Vue + TypeScript + ESLint 專案可能會遇到的坑

  • Oct 03, 2023
  • by Sean Huang

以下使用 pnpm 作為套件管理工具,與 npm 或 yarn 的指令會有些許差異,請查看隨附的官方文件

TL;DR

完整配置範例:https://github.com/ngseke/vite-vue-ts-eslint-example

🏗️ 透過 Vite 官方的 Preset 新建專案

Scaffolding Your First Vite Project

pnpm create vite my-vue-app --template vue-ts
+
pnpm create vite my-vue-app --template vue-ts
+

移動到該目錄並安裝相依

cd my-vue-app
+pnpm i
+
cd my-vue-app
+pnpm i
+

🏗️ 初始化 ESLint

Getting Started with ESLint - Quick start

pnpm create @eslint/config
+
pnpm create @eslint/config
+

依序回答以下問題後,便會在根目錄新建 .eslintrc.cjs

✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · vue
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · standard-with-typescript
✔ What format do you want your config file to be in? · JavaScript
✔ Would you like to install them now? · No / Yes
✔ Which package manager do you want to use? · pnpm

😵 處理 VSCode Output 的 ESLint 的問題

雖然 ESLint 已初始化完成,但你會發現它尚未正常運作。例如試著在任意 .ts.vue 檔隨便加多餘的空格,卻看不到預期的 error 或 warning 的波浪底線。

查看 Output 會看到以下錯誤訊息:

An unexpected error occurred:
Error: Error while loading rule ‘@typescript-eslint/dot-notation’: You have used a rule which requires parserServices to be generated. You must therefore provide a value for the “parserOptions.project” property for @typescript-eslint/parser.

根據錯誤訊息的描述,得知需要在 .eslintrc.cjs 補上 parserOptions.project 並指明 parserOptions.parser

// .eslintrc.cjs
+module.exports = {
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
++       project: ['./tsconfig.json', './tsconfig.node.json'],
++       parser: '@typescript-eslint/parser',
+    },
+}
+
// .eslintrc.cjs
+module.exports = {
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
++       project: ['./tsconfig.json', './tsconfig.node.json'],
++       parser: '@typescript-eslint/parser',
+    },
+}
+

再次查看 Output 錯誤訊息已消失

Q: 為什麼是 parserOptions.parser 而非 parser
A: 根據官方文件說明,若寫在 parser 會把 vue-eslint-parser 覆蓋掉而無法正常地 lint .vue 檔。所以當有自訂的 parser 時(例如 @typescript-eslint/parser),必須把它移入 parserOptions

😵 處理根目錄檔 TSConfig include 的問題

雖然 Output 錯誤訊息已消失,但這時用 VSCode 開啟在根目錄的 .ts.cjs 檔,例如 vite.config.ts.eslintrc.cjs,會發現它們在開頭都多了紅色的波浪底線:

Parsing error: ESLint was configured to run on <tsconfigRootDir>/.eslintrc.cjs using parserOptions.project: /users/sean/my-vue-app/tsconfig.json
However, that TSConfig does not include this file. Either:

根據錯誤訊息得知,我們必須在 tsconfig.node.jsonincludes 中手動加入這些檔案:

// tsconfig.node.json
+{
+  // ...
+  "include": [
+    "vite.config.ts",
++   ".eslintrc.cjs"
+  ],
+}
+
// tsconfig.node.json
+{
+  // ...
+  "include": [
+    "vite.config.ts",
++   ".eslintrc.cjs"
+  ],
+}
+

日後若是在根目錄有新增那些在 Node 環境執行的,而不會被實際打包進專案的設定檔,例如 Tailwind CSS 的設定檔 tailwind.config.ts,也要記得手動加入進去。

接著重新載入 VSCode 視窗

叫出指令視窗 ⌘ + shift + P → 輸入 Developer: Reload Window

打開 .eslintrc.cjs 可以看到 ESLint 終於正常運作了,它開始確實根據 eslint-config-standard-with-typescript 的預設規則進行檢查,例如多餘的引號、字串應為單引號和縮排應為 2 格空格等。

😵 處理 .vue 檔的 non-standard 問題

打開任意 .vue 檔會發現它們也出現了錯誤訊息:

Parsing error: ESLint was configured to run on <tsconfigRootDir>/src/App.vue using parserOptions.project: /users/sean/my-vue-app/tsconfig.json
The extension for the file (.vue) is non-standard. You should add parserOptions.extraFileExtensions to your config.

根據錯誤訊息的提示在 .eslintrc.cjs 加入 parserOptions.extraFileExtensions 後,再次重新載入 VSCode 視窗即可。

// .eslintrc.cjs
+{
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
+        project: ['./tsconfig.json', './tsconfig.node.json'],
+        parser: '@typescript-eslint/parser',
++       extraFileExtensions: ['.vue']
+    },
+}
+
// .eslintrc.cjs
+{
+    // ...
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module",
+        project: ['./tsconfig.json', './tsconfig.node.json'],
+        parser: '@typescript-eslint/parser',
++       extraFileExtensions: ['.vue']
+    },
+}
+

😵 跳過檢查某些檔案

dist/

我們沒必要檢查建構好的已醜化和壓縮的檔案,因此可以在 .eslintrc.cjsignorePatterns 排除掉整個 dist 目錄:

// .eslintrc.cjs
+module.exports = {
+    // ...
++   ignorePatterns: ['dist'],
+}
+
// .eslintrc.cjs
+module.exports = {
+    // ...
++   ignorePatterns: ['dist'],
+}
+

vite-env.d.ts

按照預設規則,vite-env.d.ts 會違反 @typescript-eslint/triple-slash-reference 這條規則,建議可以在 vite-env.d.ts 的開頭加上註解 // eslint-disable-next-line ... 來跳過檢查:

// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference types="vite/client" />
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference types="vite/client" />
+

✅ 大功告成

到此為止 ESLint 應該就可以順利的運作,接著你可以更進一步根據個人或團隊的風格和偏好調整 rules,開啟、關閉或調整某些規則。

例如我總是習慣微調 @typescript-eslint/comma-dangle(行末逗號)的設定,讓最後一項依然保留逗號,這樣就可以更方便調整物件成員的順序:

module.exports = {
+  // ...
+  rules: {
+    // ...
+    '@typescript-eslint/comma-dangle': ['error', {
+      arrays: 'always-multiline',
+      objects: 'always-multiline',
+      imports: 'always-multiline',
+      exports: 'always-multiline',
+      functions: 'only-multiline',
+    }],
+  },
+}
+
module.exports = {
+  // ...
+  rules: {
+    // ...
+    '@typescript-eslint/comma-dangle': ['error', {
+      arrays: 'always-multiline',
+      objects: 'always-multiline',
+      imports: 'always-multiline',
+      exports: 'always-multiline',
+      functions: 'only-multiline',
+    }],
+  },
+}
+

✨ 設定 VSCode 存檔時自動排版

你還可以讓開發體驗變的更舒適。

打開 .vscode/settings.json,若原本沒有這個檔案可以手動建立一個,加入以下設定:

{
+  // ...
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  },
+}
+
{
+  // ...
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  },
+}
+

回到剛才滿江紅的 .eslintrc.cjs 測試看看,在手動存檔(⌘ + S)後就會自動排版和修正錯誤:

✨ 透過 script 檢查錯誤或是自動修復錯誤

儘管在 VSCode 中的檢查和錯誤提示有助於得到即時反饋,但這些提示終究是 「消極的」 。如果開發者使用別款 IDE,或只是單純對提示視若無睹,他們仍然可以輕易地違反這些規則,並提交不符合規則的程式碼。

因此我們還需要透過 script 的方式來真正的執行規則檢查。

用指令檢查錯誤

打開 package.json,在 scripts 中加入指令 lint,其中 --ext 的後面是想要檢查的副檔名:

// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
++   "lint": "npx eslint . --ext .ts,.js,.cjs,.vue"
+  },
+}
+
// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
++   "lint": "npx eslint . --ext .ts,.js,.cjs,.vue"
+  },
+}
+

接著在 terminal 測試執行效果

pnpm run lint
+
pnpm run lint
+

可以看到它列出了所有不符合設定規則的 error 和 warning,並且回傳 exit code 1

這表示你就可以將這條指令整合進你的部署流程中,例如:

  1. 搭配 husky:使用 husky 可以在每次 commit 前自動執行 pnpm run lint。如果程式碼不符合規則就不給 commit,迫使開發者修復錯誤後再進行 commit,確保 code base 的風格始終維持一致
  2. CI/CD:你可以將 pnpm run lint 加入 CI/CD 流程,這樣在每次部署前都會自動進行代碼檢查。若檢查失敗,部署流程就會自動中斷,讓開發者不得不修正錯誤

用指令自動修復錯誤

打開 package.json,在 scripts 中加入以下指令 lint:fix

// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
+    "lint": "npx eslint . --ext .ts,.js,.cjs,.vue",
++   "lint:fix": "npx eslint --fix . --ext .ts,.js,.cjs,.vue"
+  },
+}
+
// package.json
+{
+  // ...
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview",
+    "lint": "npx eslint . --ext .ts,.js,.cjs,.vue",
++   "lint:fix": "npx eslint --fix . --ext .ts,.js,.cjs,.vue"
+  },
+}
+

接著在 terminal 測試執行效果

pnpm run lint:fix
+
pnpm run lint:fix
+

順利的話「可自動修正(automatically fixable)」的那些規則都會被自動修正,也就是在 Rule 列表有 🔧 符號的那些項目。

ESLint 即將棄用「排版」規則

ESLint 在 2023 年 10 月宣布將棄用排版(Formatting)規則,也就是棄用那些跟空格、縮排、換行、單/雙引號、分號等相關規則。而其餘分類下的規則不受影響,例如強制使用嚴格等於 ===eqeqeq)、強制命名小駝峰(camelcase)等。

未來若想繼續透過 ESLint 而非 Prettier 來排版程式碼,可以考慮搭配 ESLint Stylistic 來達成一樣的效果。這個 plugin 將會繼續接棒,維護這些被棄用的規則。

參考資料

https://github.com/vitejs/vite/issues/13739#issuecomment-1641380518
https://juejin.cn/post/7126043888573218823

+ + + + \ No newline at end of file diff --git a/blog/webpack-config-esm.html b/blog/webpack-config-esm.html new file mode 100644 index 00000000..d54a95b7 --- /dev/null +++ b/blog/webpack-config-esm.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + 在 webpack.config.js 裡使用 ESM 的 import 語法 | ngseke + +
  • Webpack

在 webpack.config.js 裡使用 ESM 的 import 語法

  • Feb 20, 2022
  • by Sean Huang

步驟

安裝 @babel/registerbabel-preset-env

npm install -D @babel/core @babel/register babel-preset-env
+
npm install -D @babel/core @babel/register babel-preset-env
+

設定 .babelrc

{
+  "presets": [
++   "@babel/preset-env"
+  ],
+}
+
+
{
+  "presets": [
++   "@babel/preset-env"
+  ],
+}
+
+

更新 webpack 設定

  1. 將原 webpack.config.js 更名為 webpack.config.babel.js
  2. 在專案根目錄建立 webpack.config.js
    require('@babel/register')
    +module.exports = require('./webpack.config.babel.js')
    +
    require('@babel/register')
    +module.exports = require('./webpack.config.babel.js')
    +

參考資料

+ + + + \ No newline at end of file diff --git a/blog/wikipedia-link-converter.html b/blog/wikipedia-link-converter.html new file mode 100644 index 00000000..aa883c44 --- /dev/null +++ b/blog/wikipedia-link-converter.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + 還給你 Google 搜尋結果的桌面版維基百科 | ngseke + +
  • Tampermonkey

還給你 Google 搜尋結果的桌面版維基百科

  • May 27, 2022
  • by Sean Huang

腳本連結

https://gist.github.com/ngseke/5d0f9ef02aa320e969768aaa8e5d888a

說明

不知道從何時開始,Google 搜尋結果的維基百科連結,在不知不覺間都變成了行動版本。然而行動版在筆電上總有一股違和感,也可能是我早已看習慣舊版的了。

於是我寫了一段簡單的 Tampermonkey 腳本,用來轉換 Google 搜尋結果中所有的維基百科連結,將行動版轉換成桌面版,也就是把網址的 .m 部分給去掉。

轉換結果

Before:
before

After:
after

+ + + + \ No newline at end of file diff --git a/favicon.png b/favicon.png new file mode 100755 index 00000000..55cf646b Binary files /dev/null and b/favicon.png differ diff --git a/fe.html b/fe.html new file mode 100644 index 00000000..94647fce --- /dev/null +++ b/fe.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/files/TypingTyping.zip b/files/TypingTyping.zip new file mode 100644 index 00000000..5cf5577c Binary files /dev/null and b/files/TypingTyping.zip differ diff --git a/img/og/blog-bootstrap-in-nuxt.png b/img/og/blog-bootstrap-in-nuxt.png new file mode 100644 index 00000000..4eb7bf85 Binary files /dev/null and b/img/og/blog-bootstrap-in-nuxt.png differ diff --git a/img/og/blog-check-if-key-exists.png b/img/og/blog-check-if-key-exists.png new file mode 100644 index 00000000..39de97ed Binary files /dev/null and b/img/og/blog-check-if-key-exists.png differ diff --git a/img/og/blog-code-review-taxi-number.png b/img/og/blog-code-review-taxi-number.png new file mode 100644 index 00000000..84226f90 Binary files /dev/null and b/img/og/blog-code-review-taxi-number.png differ diff --git a/img/og/blog-component-naming.png b/img/og/blog-component-naming.png new file mode 100644 index 00000000..e41fb8c6 Binary files /dev/null and b/img/og/blog-component-naming.png differ diff --git a/img/og/blog-customize-youtube-caption-font.png b/img/og/blog-customize-youtube-caption-font.png new file mode 100644 index 00000000..74738414 Binary files /dev/null and b/img/og/blog-customize-youtube-caption-font.png differ diff --git a/img/og/blog-react-handler-type.png b/img/og/blog-react-handler-type.png new file mode 100644 index 00000000..ca47ac4b Binary files /dev/null and b/img/og/blog-react-handler-type.png differ diff --git a/img/og/blog-reproduce-bootstrap-grid-in-tailwind.png b/img/og/blog-reproduce-bootstrap-grid-in-tailwind.png new file mode 100644 index 00000000..7ea01b28 Binary files /dev/null and b/img/og/blog-reproduce-bootstrap-grid-in-tailwind.png differ diff --git a/img/og/blog-typescript-as-const.png b/img/og/blog-typescript-as-const.png new file mode 100644 index 00000000..bb19b06d Binary files /dev/null and b/img/og/blog-typescript-as-const.png differ diff --git a/img/og/blog-vite-vue-ts-eslint-setup.png b/img/og/blog-vite-vue-ts-eslint-setup.png new file mode 100644 index 00000000..22397d7d Binary files /dev/null and b/img/og/blog-vite-vue-ts-eslint-setup.png differ diff --git a/img/og/blog-webpack-config-esm.png b/img/og/blog-webpack-config-esm.png new file mode 100644 index 00000000..295c4d2e Binary files /dev/null and b/img/og/blog-webpack-config-esm.png differ diff --git a/img/og/blog-wikipedia-link-converter.png b/img/og/blog-wikipedia-link-converter.png new file mode 100644 index 00000000..1072f904 Binary files /dev/null and b/img/og/blog-wikipedia-link-converter.png differ diff --git a/img/og/project-boss.png b/img/og/project-boss.png new file mode 100644 index 00000000..54316e5f Binary files /dev/null and b/img/og/project-boss.png differ diff --git a/img/og/project-camp-2017.png b/img/og/project-camp-2017.png new file mode 100644 index 00000000..7630f308 Binary files /dev/null and b/img/og/project-camp-2017.png differ diff --git a/img/og/project-camp2017.png b/img/og/project-camp2017.png new file mode 100644 index 00000000..7630f308 Binary files /dev/null and b/img/og/project-camp2017.png differ diff --git a/img/og/project-credit-card-calc.png b/img/og/project-credit-card-calc.png new file mode 100644 index 00000000..50a6ffba Binary files /dev/null and b/img/og/project-credit-card-calc.png differ diff --git a/img/og/project-em-optimization-lab.png b/img/og/project-em-optimization-lab.png new file mode 100644 index 00000000..c0eabfbe Binary files /dev/null and b/img/og/project-em-optimization-lab.png differ diff --git a/img/og/project-emo.png b/img/og/project-emo.png new file mode 100644 index 00000000..c0eabfbe Binary files /dev/null and b/img/og/project-emo.png differ diff --git a/img/og/project-flag.png b/img/og/project-flag.png new file mode 100644 index 00000000..b84bfd32 Binary files /dev/null and b/img/og/project-flag.png differ diff --git a/img/og/project-gomoku.png b/img/og/project-gomoku.png new file mode 100644 index 00000000..eaea8517 Binary files /dev/null and b/img/og/project-gomoku.png differ diff --git a/img/og/project-iphone-price.png b/img/og/project-iphone-price.png new file mode 100644 index 00000000..e8a40dce Binary files /dev/null and b/img/og/project-iphone-price.png differ diff --git a/img/og/project-koasu.png b/img/og/project-koasu.png new file mode 100644 index 00000000..ef90d163 Binary files /dev/null and b/img/og/project-koasu.png differ diff --git a/img/og/project-leetcode-night.png b/img/og/project-leetcode-night.png new file mode 100644 index 00000000..82d364c5 Binary files /dev/null and b/img/og/project-leetcode-night.png differ diff --git a/img/og/project-mcip-cms.png b/img/og/project-mcip-cms.png new file mode 100644 index 00000000..fce74b91 Binary files /dev/null and b/img/og/project-mcip-cms.png differ diff --git a/img/og/project-mcip.png b/img/og/project-mcip.png new file mode 100644 index 00000000..baa573b8 Binary files /dev/null and b/img/og/project-mcip.png differ diff --git a/img/og/project-raise-your-red-flag.png b/img/og/project-raise-your-red-flag.png new file mode 100644 index 00000000..b84bfd32 Binary files /dev/null and b/img/og/project-raise-your-red-flag.png differ diff --git a/img/og/project-shanlinliang.png b/img/og/project-shanlinliang.png new file mode 100644 index 00000000..83c26c47 Binary files /dev/null and b/img/og/project-shanlinliang.png differ diff --git a/img/og/project-tic-tac-toe.png b/img/og/project-tic-tac-toe.png new file mode 100644 index 00000000..c0a18e7d Binary files /dev/null and b/img/og/project-tic-tac-toe.png differ diff --git a/img/og/project-typing-typing.png b/img/og/project-typing-typing.png new file mode 100644 index 00000000..e06231b1 Binary files /dev/null and b/img/og/project-typing-typing.png differ diff --git a/img/og/project-typingtyping.png b/img/og/project-typingtyping.png new file mode 100644 index 00000000..e06231b1 Binary files /dev/null and b/img/og/project-typingtyping.png differ diff --git a/img/og/project-versatile-npm.png b/img/og/project-versatile-npm.png new file mode 100644 index 00000000..d42fee16 Binary files /dev/null and b/img/og/project-versatile-npm.png differ diff --git a/img/project-cover/boss.png b/img/project-cover/boss.png new file mode 100755 index 00000000..cb3441c6 Binary files /dev/null and b/img/project-cover/boss.png differ diff --git a/img/project-cover/camp2017.png b/img/project-cover/camp2017.png new file mode 100755 index 00000000..9f087e61 Binary files /dev/null and b/img/project-cover/camp2017.png differ diff --git a/img/project-cover/credit-card-calc.png b/img/project-cover/credit-card-calc.png new file mode 100755 index 00000000..46004a40 Binary files /dev/null and b/img/project-cover/credit-card-calc.png differ diff --git a/img/project-cover/emo.png b/img/project-cover/emo.png new file mode 100755 index 00000000..84fc237b Binary files /dev/null and b/img/project-cover/emo.png differ diff --git a/img/project-cover/flag.png b/img/project-cover/flag.png new file mode 100755 index 00000000..0d96deae Binary files /dev/null and b/img/project-cover/flag.png differ diff --git a/img/project-cover/flip-card.png b/img/project-cover/flip-card.png new file mode 100755 index 00000000..ef943337 Binary files /dev/null and b/img/project-cover/flip-card.png differ diff --git a/img/project-cover/gomoku.png b/img/project-cover/gomoku.png new file mode 100755 index 00000000..0abc9d1e Binary files /dev/null and b/img/project-cover/gomoku.png differ diff --git a/img/project-cover/iphone-price.png b/img/project-cover/iphone-price.png new file mode 100644 index 00000000..afc8c542 Binary files /dev/null and b/img/project-cover/iphone-price.png differ diff --git a/img/project-cover/koasu.png b/img/project-cover/koasu.png new file mode 100755 index 00000000..dc5053c7 Binary files /dev/null and b/img/project-cover/koasu.png differ diff --git a/img/project-cover/leetcode-night.png b/img/project-cover/leetcode-night.png new file mode 100755 index 00000000..e3606d86 Binary files /dev/null and b/img/project-cover/leetcode-night.png differ diff --git a/img/project-cover/mcip-cms.png b/img/project-cover/mcip-cms.png new file mode 100755 index 00000000..831337d6 Binary files /dev/null and b/img/project-cover/mcip-cms.png differ diff --git a/img/project-cover/mcip.png b/img/project-cover/mcip.png new file mode 100755 index 00000000..eeac6ece Binary files /dev/null and b/img/project-cover/mcip.png differ diff --git a/img/project-cover/shanlinliang.png b/img/project-cover/shanlinliang.png new file mode 100755 index 00000000..3b9fc5c7 Binary files /dev/null and b/img/project-cover/shanlinliang.png differ diff --git a/img/project-cover/taiwan-company-blocker.png b/img/project-cover/taiwan-company-blocker.png new file mode 100644 index 00000000..3d74103a Binary files /dev/null and b/img/project-cover/taiwan-company-blocker.png differ diff --git a/img/project-cover/tic-tac-toe.png b/img/project-cover/tic-tac-toe.png new file mode 100644 index 00000000..d9994e8e Binary files /dev/null and b/img/project-cover/tic-tac-toe.png differ diff --git a/img/project-cover/typing-typing.png b/img/project-cover/typing-typing.png new file mode 100755 index 00000000..d2cdf6bc Binary files /dev/null and b/img/project-cover/typing-typing.png differ diff --git a/img/project-cover/versatile-npm.png b/img/project-cover/versatile-npm.png new file mode 100644 index 00000000..0bbde45b Binary files /dev/null and b/img/project-cover/versatile-npm.png differ diff --git a/index.html b/index.html new file mode 100644 index 00000000..3efad8cd --- /dev/null +++ b/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + ngseke + +
@ngseke

Hi, I'm Sean. 🍻

現職前端軟體工程師,專注於 TypeScriptVueReact
追求撰寫無瑕程式碼是我的開發格言,並致力於優化開發者體驗。
熱衷探究前端領域的新鮮事,期待藉由知識分享為社群創造影響力。

aka 黃省喬 / HUANG Sing-Ciao

Skills

  • React
  • Vue 3
  • Tailwind CSS
  • Nuxt
  • TypeScript
  • Node.js

Experience

  • Software Engineer @ ASUS AICS
    2022 — 2023
  • Software Engineer @ 3drens
    2021 — 2022
  • Software Engineer @ gogoout
    2019 — 2021
  • Summer Engineering Intern @ Hiero7
    Summer 2018

Contact Me

Projects

  • Versatile Npm

    自訂 Npm 安裝指令瀏覽器擴充功能

  • LeetCode Night

    LeetCode 深色模式瀏覽器擴充功能

Recent Posts

+ + + + \ No newline at end of file diff --git a/project.html b/project.html new file mode 100644 index 00000000..43f74522 --- /dev/null +++ b/project.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + + Projects | ngseke + +

Browser Extension

  • Taiwan Company Blocker

    台灣求職網封鎖神器

  • Versatile Npm

    自訂 Npm 安裝指令瀏覽器擴充功能

  • LeetCode Night

    LeetCode 深色模式瀏覽器擴充功能

Web

  • iPhone Price

    台灣 iPhone 價格歷史趨勢

  • Koasu

    KOASÛ 白話字台語歌詞網誌

  • MCIP Official Website

    《樂台計畫》官方網站

  • MCIP CMS

    《樂台計畫》後台管理系統

  • Sinopac Dual Currency Card Calc.

    永豐幣倍卡回饋計算機

  • EM Optimization Lab

    《電磁最佳化實驗室》網站

System Design

  • BOSS: Beverage Online Shop System

    線上飲料購物系統

Game

  • Flip Card

    Emoji 翻牌配對遊戲

  • Gomoku

    線上五子棋對戰

  • Tic Tac Toe

    圈圈叉叉亂鬥

  • Raise Your Red Flag

    以 Webcam 重現經典團康遊戲《紅旗舉起來》

  • Typing Typing!

    8-bit 復古風格打字遊戲

Identity Design

  • NTUT-CSIE & NTUB-ACC Camp, 2017

    迎新《會炒不加辣,果資不加糖》品牌識別設計

  • Shanlinliang

    虛構涼扇品牌廣告《扇林涼》

+ + + + \ No newline at end of file diff --git a/project/boss.html b/project/boss.html new file mode 100644 index 00000000..42a33711 --- /dev/null +++ b/project/boss.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + BOSS: Beverage Online Shop System | ngseke + +

BOSS: Beverage Online Shop System

線上飲料購物系統

  • PHP
  • MySQL
  • UI/UX
  • Nov, 2017 - Jan, 2018
  • Team Member: 吳品頤余鎧企黃省喬趙振廷

這是一個在線飲料購物系統 (Beverage Online Shop System),簡稱BOSS,是一個簡單易用,介面時尚清爽的飲料購物平台。本專案後端以PHP語言撰寫,前端以Bootstrap的架構搭配HTML5和CSS3作為基礎進行版型設計。

在此系統中可以根據不同使用者身份,包括顧客、員工及管理員,提供各種不同的操作功能。
我們提供簡單直覺易懂的操作界面,讓顧客可以瀏覽本店所有商品、根據分類或搜尋條件取得商品資訊、對於產品及訂單進行打分或評論、查看。
員工可以上下架商品、管理商品之庫存及資訊、查看當前所有訂單資訊。管理者可以新增移除或修改會員資訊。

介面

隨機展示熱銷商品
顯示當前優惠活動
商品列表
商品詳情頁面

Demo

http://boss.ngseke.me/ 已下架

+ + + + \ No newline at end of file diff --git a/project/camp-2017.html b/project/camp-2017.html new file mode 100644 index 00000000..43bd5835 --- /dev/null +++ b/project/camp-2017.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + NTUT-CSIE & NTUB-ACC Camp, 2017 | ngseke + +

NTUT-CSIE & NTUB-ACC Camp, 2017

迎新《會炒不加辣,果資不加糖》品牌識別設計

  • Illustrator
  • Final Cut Pro
  • Jul, 2017 - Aug, 2017

2017 年秋季北科資工系北商會資系聯合舉辦的迎新活動,使用 Adobe Illustrator 設計與排版,圖標源自 Flaticon

Logo 分別展現出快炒鍋、辣椒、果汁和方糖的意象,與活動名稱相呼應。

Logo圖標
Logo文字
英文版Logo文字
布幕

名牌

名牌
名牌(英文)

T-SHIRT

營T
組T(背後)

Facebook 粉專

大頭貼
橫幅
粉專實際使用情境
提醒事項
大合照標題

Facebook 大頭貼

供工人宣傳期間替換的大頭貼,另有無頭版本可自由定制。
標準版
無頭版
定制範例 1
定制範例 2
大頭貼實際使用情境

小手冊








小手冊印刷成品 1
小手冊實際印刷成品 2

回顧影片

使用 Final Cut Pro 剪輯。

工人回顧影片

三日學員回顧影片

+ + + + \ No newline at end of file diff --git a/project/camp2017.html b/project/camp2017.html new file mode 100644 index 00000000..43bd5835 --- /dev/null +++ b/project/camp2017.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + NTUT-CSIE & NTUB-ACC Camp, 2017 | ngseke + +

NTUT-CSIE & NTUB-ACC Camp, 2017

迎新《會炒不加辣,果資不加糖》品牌識別設計

  • Illustrator
  • Final Cut Pro
  • Jul, 2017 - Aug, 2017

2017 年秋季北科資工系北商會資系聯合舉辦的迎新活動,使用 Adobe Illustrator 設計與排版,圖標源自 Flaticon

Logo 分別展現出快炒鍋、辣椒、果汁和方糖的意象,與活動名稱相呼應。

Logo圖標
Logo文字
英文版Logo文字
布幕

名牌

名牌
名牌(英文)

T-SHIRT

營T
組T(背後)

Facebook 粉專

大頭貼
橫幅
粉專實際使用情境
提醒事項
大合照標題

Facebook 大頭貼

供工人宣傳期間替換的大頭貼,另有無頭版本可自由定制。
標準版
無頭版
定制範例 1
定制範例 2
大頭貼實際使用情境

小手冊








小手冊印刷成品 1
小手冊實際印刷成品 2

回顧影片

使用 Final Cut Pro 剪輯。

工人回顧影片

三日學員回顧影片

+ + + + \ No newline at end of file diff --git a/project/credit-card-calc.html b/project/credit-card-calc.html new file mode 100644 index 00000000..8cc6ad8f --- /dev/null +++ b/project/credit-card-calc.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Sinopac Dual Currency Card Calc. | ngseke + +

Sinopac Dual Currency Card Calc.

永豐幣倍卡回饋計算機

  • Vue
  • TypeScript
  • Tailwind CSS
  • Vite
  • UI/UX
  • Jun, 2021

簡明的信用卡回饋計算機,輸入刷卡金額並選擇帳戶資格即可試算回饋金額。

此專案採用 Vue 3 的各種新特性,如 Composition API、 <script setup>,並引入 TypeScript 以獲得無縫的開發體驗。

組件完全以 Tailwind CSS 建構,透過 Dark Mode 的 class strategyVueUseuseDark() 來輕鬆實現深色模式。

Demo

https://ngseke.github.io/sinopac-dual-currency-card-calculator/

(註:一代神卡已殞落,2022 年後已不再適用此回饋規則)

+ + + + \ No newline at end of file diff --git a/project/em-optimization-lab.html b/project/em-optimization-lab.html new file mode 100644 index 00000000..9c1e4ead --- /dev/null +++ b/project/em-optimization-lab.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + EM Optimization Lab | ngseke + +

EM Optimization Lab

《電磁最佳化實驗室》網站

  • Vue
  • Firebase
  • gulp.js
  • UI/UX
  • Mar, 2018 - Oct, 2018

這是台北科技大學之電磁最佳化實驗室 (EM Optimization Lab) 的網站,包含 RWD 來增進不同裝置上的瀏覽體驗。

前端主要以 Vue 架構,使用 Sass 和 Pug 搭配 Gulp 自動化流程工具編譯,使用 Firebase 作為資料庫。

首頁

後台管理

網站提供了簡單易用的後台介面,讓管理者能方便快速地更新頁面內容,如修改首頁近期活動上傳輪播照片修改實驗室成員 等。

考慮到網站部署空間的限制,因為學校提供的上傳空間只支援靜態網頁,所以資料庫選擇使用 Firebase,其最大的特色就是只靠靜態的前端網頁就可以操作 database。此外它也可以靠 JS 來上傳檔案到儲存空間,也提供驗證登入的功能,讓擁有權限的使用者才可進入後台編輯。

登入後台
後台介面
編輯研究項目
編輯實驗室成員
上傳輪播照片(可拖曳排序)

Demo

https://myweb.ntut.edu.tw/~yschen/

+ + + + \ No newline at end of file diff --git a/project/emo.html b/project/emo.html new file mode 100644 index 00000000..3469cf0d --- /dev/null +++ b/project/emo.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + EM Optimization Lab | ngseke + +

EM Optimization Lab

《電磁最佳化實驗室》網站

  • Vue
  • Firebase
  • gulp.js
  • UI/UX
  • Mar, 2018 - Oct, 2018

這是台北科技大學之電磁最佳化實驗室 (EM Optimization Lab) 的網站,包含 RWD 來增進不同裝置上的瀏覽體驗。

前端主要以 Vue 架構,使用 Sass 和 Pug 搭配 Gulp 自動化流程工具編譯,使用 Firebase 作為資料庫。

首頁

後台管理

網站提供了簡單易用的後台介面,讓管理者能方便快速地更新頁面內容,如修改首頁近期活動上傳輪播照片修改實驗室成員 等。

考慮到網站部署空間的限制,因為學校提供的上傳空間只支援靜態網頁,所以資料庫選擇使用 Firebase,其最大的特色就是只靠靜態的前端網頁就可以操作 database。此外它也可以靠 JS 來上傳檔案到儲存空間,也提供驗證登入的功能,讓擁有權限的使用者才可進入後台編輯。

登入後台
後台介面
編輯研究項目
編輯實驗室成員
上傳輪播照片(可拖曳排序)

Demo

https://myweb.ntut.edu.tw/~yschen/

+ + + + \ No newline at end of file diff --git a/project/flag.html b/project/flag.html new file mode 100644 index 00000000..462ab680 --- /dev/null +++ b/project/flag.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Raise Your Red Flag | ngseke + +

Raise Your Red Flag

以 Webcam 重現經典團康遊戲《紅旗舉起來》

  • Jun, 2018

數位影像處理期末專題。

藉助 tracking.js 的力量,在瀏覽器以 Webcam 遊玩,重溫耳熟能詳的團康遊戲,一起成為那支舉起專家

試玩影片

Demo

https://raise-flag.web.app/

+ + + + \ No newline at end of file diff --git a/project/gomoku.html b/project/gomoku.html new file mode 100644 index 00000000..9e8fb537 --- /dev/null +++ b/project/gomoku.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Gomoku | ngseke + +

Gomoku

線上五子棋對戰

  • Vue
  • Firebase
  • UI/UX
  • Oct, 2018 - Nov, 2018
+ + + + \ No newline at end of file diff --git a/project/iphone-price.html b/project/iphone-price.html new file mode 100644 index 00000000..5b04d2af --- /dev/null +++ b/project/iphone-price.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + iPhone Price | ngseke + +

iPhone Price

台灣 iPhone 價格歷史趨勢

  • Vue
  • TypeScript
  • Tailwind
  • daisyUI
  • ECharts
  • UI/UX
  • Oct, 2023
+ + + + \ No newline at end of file diff --git a/project/koasu.html b/project/koasu.html new file mode 100644 index 00000000..c86390c1 --- /dev/null +++ b/project/koasu.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Koasu | ngseke + +

Koasu

KOASÛ 白話字台語歌詞網誌

  • Hexo
  • Aug, 2021 - Present

KOASÛ 也就是台語「歌詞koa-sû」的白話字。

本網誌基於 Hexo 架設,主要受到台客鬼歌詞正字的啟發,補全它們未收錄的台語歌詞,特別是某些非主流歌曲,例如來去高雄。本人將這些歌詞翻譯成符合教育部所規範的台文正字,並以白話字表記發音,也希望藉此過程來提高自己對白話字的掌握度。

台文漢字的選用與拼法主要參考臺灣閩南語常用詞辭典萌典iTaigi 愛台語

Demo

https://ngseke.github.io/koasu/

+ + + + \ No newline at end of file diff --git a/project/leetcode-night.html b/project/leetcode-night.html new file mode 100644 index 00000000..bf31fa29 --- /dev/null +++ b/project/leetcode-night.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + LeetCode Night | ngseke + +

LeetCode Night

LeetCode 深色模式瀏覽器擴充功能

  • React
  • TypeScript
  • i18n
  • styled-components
  • UI/UX
  • Feb, 2022 - Present

長久以來都只有中國版力扣支援完整的暗黑模式,而美國 LeetCode 主站則僅有在 Problem List、Profile 等少數頁面頁才支援,獨缺解題頁。

某夜在深夜刷題的我突發奇想,說不定我可以取用力扣的色票,直接複寫 LeetCode 的樣式來實現深色模式,於是便誕生了這個專案。

原本也僅僅是寫好玩,就把它扔上了 GitHub,甚至連名字都是隨意取的。後來發現其實蠻多人都在跟官方敲碗深色模式。因此後來我決定把它上架到商店,讓它變得更容易安裝。對我來說,這也是個難得可以從頭到尾,完整地開發擴充套件的機會。過程除了前端相關的開發、打包、上版號,甚至也包含 UI 和 Logo 設計 、截圖和宣傳橫幅的設計、發布送審套件等。

經過了半年左右時間,使用者人數終於突破了 1K,還獲得了藍勾 & 精選商品徽章,也算是達成了不值一提的小小成就。

簡介

LeetCode Night 是 Google Chrome 的擴充功能,讓使用者在題目頁也可以套用深色模式,即使在夜晚刷題也不必擔心瞎眼。

  • TypeScript
  • UI 組件基於 Tocas UI v4 建構,搭配 styled-component 自定義樣式
  • 使用 Axios 和 GraphQL 請求題目列表
  • 使用 react-i18next 本地化
  • 透過 Webpack css-loader 向頁面注入複寫用的 SASS 樣式

Popup 視窗

此外還有附加以下便利功能:

  1. 反相圖片顏色,讓題目中多數白底的插圖,在深色模式中也不至於太突兀
  2. 輸入 Question Number 來迅速跳轉至題目頁,此插件會將題目列表緩存至本機,無需等待 API 回應
  3. 自動清空編輯器裡前一次送出過的程式碼,特別適合刷題狂人,避免不小心瞄到答案

啟用 LeetCode 深色模式

Demo


+ + + + \ No newline at end of file diff --git a/project/mcip-cms.html b/project/mcip-cms.html new file mode 100644 index 00000000..976e59e5 --- /dev/null +++ b/project/mcip-cms.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + MCIP CMS | ngseke + +

MCIP CMS

《樂台計畫》後台管理系統

  • Vue
  • Vuetify
  • ECharts
  • UI/UX
  • Nov, 2018 - Present

樂台計畫誕生於 2018 年之冬,是我與大學好友鎧企(K7)攜手開發的大專院校音樂平台。我們皆來自吉他社,體認到各個賽事在籌辦時的痛點,於是樂台計畫應運而生。樂台計畫專為音樂賽事量身打造,宗旨是建構更優質的賽事環境,簡化社團在處理報名業務的作業流程。

服務上線至今,樂台計畫傾聽來自各方使用者的回饋和建議,持續優化系統與擴展規模。

對於參賽者,樂台計畫以 Line App 官方帳號的形式,讓使用者輕鬆簡單地報名參賽、瀏覽各賽事詳情,甚至可以查看當前比賽進度。目前使用者數已超過 6,000 人,全台從北到南,共計有 26 所大專院校的音樂社團成為我們的合作夥伴。

而對於主辦比賽的社團幹部們,樂台計畫提供一套完整的後台管理系統。在此專案中我主要負責的是後台管理系統前端開發,與一部分 LINE App 的 UI/UX

管理後台概覽頁面(Dashboard)

管理後台大部分組件取自 Vuetify ,得益於這些開箱即用的 Material Design 風格組件,讓我們可以更專注在功能需求上,在初期得以快速地迭代原型。

參賽者報名列表
編輯報名表單

此系統落實前後端分離,使用 Axios 與後端串接資料。我與後端合力規劃並協調 API 的規格,當中包含了 Database Schema 設計。在樂台計畫草創期,我們根據業務邏輯並考慮 NoSQL 的特性,妥善設計出合理的系統架構。
登入畫面

本專案使用 Pug 模板語言撰寫,透過縮排便能很靈活地調整 HTML 的巢狀結構。

專案中使用的 Pug 模板

編輯賽況資訊
設定學校資訊

  • MCIP Official Website

    《樂台計畫》官方網站

+ + + + \ No newline at end of file diff --git a/project/mcip.html b/project/mcip.html new file mode 100644 index 00000000..4814f90c --- /dev/null +++ b/project/mcip.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + MCIP Official Website | ngseke + +

MCIP Official Website

《樂台計畫》官方網站

  • Nuxt
  • Schema.org
  • UI/UX
  • Feb, 2019 - Present

樂台計畫官方網站是以 Nuxt 建構的 SSR 形象網站。在此專案我負責的項目,包含定義網站架構與規格、UI/UX 設計、開發與部署。

關於《樂台計畫》平台詳細介紹請見MCIP CMS

Cover
LINE QRCode & Statistics
News & Partners

緣由

樂台計畫在初期並未規劃官方網站,但隨著規模逐漸成長,有越來越多人與音樂社團對我們感到好奇,這讓我們重新思考品牌形象的建立與加強推廣。為了能接觸到更多群眾,讓更多人可以透過 Facebook 粉絲專頁以外的管道來認識我們,遂開始著手官方網站的構思。

舊版網站

在原本的前期版本,網站的定位只是一個陽春的入口網站,用來引導使用者加入樂台計畫 LINE App。因此當時是朝著單頁式網站的方向來設計,畫面上也只放置了 QRCode 等必要資訊。

為了盡快的製作出 prototype,當時選用 Parcel 這個易上手的打包工具。只要設定好進入點,它就會自動分析所有相依的資源,並自動封裝成 bundle,也省去很多繁複的手動配置。

陽春的初版網站首頁

以 Nuxt 重新架構

隨著網站的內容的不斷擴充,逐漸加入了「合作院校」、「聯絡我們」、「最新消息」和「FAQ」等區塊,我開始思考該如何優化網站 SEO,於是在後期決定採用 Nuxt 這個 SSR 解決方案,從頭開始打造新的網站。

在遷移到 Nuxt 後有許多好處,最顯而易見的就是預先抓取非同步資料,將內容渲染在頁面後才傳送到 client 端。其他像是 title、meta description 和 Open Graph 標籤等,也可以透過 Vue Meta 統一管理,一併預先渲染出來,有助搜尋引擎的爬蟲擷取頁面資料。

例如在社群媒體或是通訊軟體分享最新消息的文章時,縮圖和文章內容就能夠順利的被平台抓取,直接秀出預覽的資訊,提升使用者對連結的興趣。

Messenger 分享連結預覽

部署

此專案部署在 Vercel 上,堪稱是最最無腦的託管平台。基本上只需要按照官方指南,在根目錄建立配置檔 vercel.json,接著在平台上綁定 GitHub 版本庫即可。

在預設情況下 Vercel 就內建了 CI/CD,每當任何一個分支有新的 push,它都會立即執行建構和部署,並針對每個 commit 都產生出一個預覽用的 domain,方便驗證線上的結果。

Vercel Deployment

Landing Page 的 Logo 動畫

我為識別 Logo 設計了簡單 SVG 動畫,藉著操控 stroke-dashoffsetstroke-dasharray 屬性,僅靠純 CSS3 就能達到酷炫的虛線描邊特效。

Logo 動畫示意

Demo

https://mcip.app/

  • MCIP CMS

    《樂台計畫》後台管理系統

+ + + + \ No newline at end of file diff --git a/project/raise-your-red-flag.html b/project/raise-your-red-flag.html new file mode 100644 index 00000000..462ab680 --- /dev/null +++ b/project/raise-your-red-flag.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Raise Your Red Flag | ngseke + +

Raise Your Red Flag

以 Webcam 重現經典團康遊戲《紅旗舉起來》

  • Jun, 2018

數位影像處理期末專題。

藉助 tracking.js 的力量,在瀏覽器以 Webcam 遊玩,重溫耳熟能詳的團康遊戲,一起成為那支舉起專家

試玩影片

Demo

https://raise-flag.web.app/

+ + + + \ No newline at end of file diff --git a/project/shanlinliang.html b/project/shanlinliang.html new file mode 100644 index 00000000..104a4967 --- /dev/null +++ b/project/shanlinliang.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Shanlinliang | ngseke + +

Shanlinliang

虛構涼扇品牌廣告《扇林涼》

  • Illustrator
  • Oct, 2017

《扇林涼 Shanlinliang》 是一個虛構的涼扇品牌,運用簡約設計文字營造出商品的高級感。


+ + + + \ No newline at end of file diff --git a/project/tic-tac-toe.html b/project/tic-tac-toe.html new file mode 100644 index 00000000..857d1e88 --- /dev/null +++ b/project/tic-tac-toe.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Tic Tac Toe | ngseke + +

Tic Tac Toe

圈圈叉叉亂鬥

  • Vue
  • gulp
  • Firebase
  • UI/UX
  • Oct, 2018


簡直不可理喻的圈圈叉叉遊戲。在這個星球上同一時間,最多只能供兩個人類遊玩。

有附帶聊天室,提供玩家文明交流的空間。

Demo

https://ngseke.github.io/tic-tac-toe/

+ + + + \ No newline at end of file diff --git a/project/typing-typing.html b/project/typing-typing.html new file mode 100644 index 00000000..751a31c8 --- /dev/null +++ b/project/typing-typing.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Typing Typing! | ngseke + +

Typing Typing!

8-bit 復古風格打字遊戲

  • C++
  • Feb, 2017 - Jun, 2017
  • Team Member: 余鎧企黃省喬

物件導向程式設計期末專題。

這是以 ZType 作為靈感來源而開發的打字射擊遊戲,以 Visual Studio 2015 建立專案,基於 Game Framework 4.8 以 C++ 語言撰寫的 Windows 平台遊戲。

主選單

試玩影片

簡介

玩法

在本遊戲中有各種不同外貌的敵人(Enemy),但是他們都有一個共同點,那就是他們的身上都帶著一組英文單字。玩家必須在敵人接近之前,輸入其身上的單字,才能成功的把的敵人消滅。


規則

本遊戲除了普通的敵人外,另外還有多種強大的Boss,除了字數較長外,還各自擁有不同的技能。

特殊功能

當身邊有太多敵人即將接近,而且來不及將他們消滅的時候,玩家在每場遊戲,有三次的機會可以使用技能。按下"Enter"釋放出電磁脈衝(EMP),快速的將身邊的敵人消滅。

圖形/美術

我們遊戲圖形是以8 BITS復古風作為設計風格,它最大的特色就是在圖形的邊緣,看起來會有明顯的鋸齒。為了達成像素畫(Pixel Art) 的美術風格,大部分的圖形我們都是利用小畫家來繪製。和一般的繪圖軟體相比,小畫家非常適合來製作點陣圖,因為他可以很精確的在每一格中填色,並且可以很輕易的輸出Bmp格式的圖片,正好符合我們的需求。

說明畫面







角色選擇



遊戲畫面

輸入怪物身上對應的單字攻擊怪物
BOSS怪物會發射小怪攻擊玩家
按下空白鍵可以釋放EMP擊退怪物
通過關卡
Game Over

其他介面

暫停視窗
最高紀錄
遊玩記錄
清除遊玩記錄
關於頁面

遊戲下載

TypingTyping.zip (7.7mb, Windows only)

+ + + + \ No newline at end of file diff --git a/project/typingtyping.html b/project/typingtyping.html new file mode 100644 index 00000000..f87093c1 --- /dev/null +++ b/project/typingtyping.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Typing Typing! | ngseke + +

Typing Typing!

8-bit 復古風格打字遊戲

  • C++
  • Feb, 2017 - Jun, 2017
  • Team Member: 余鎧企黃省喬

物件導向程式設計期末專題。

這是以 ZType 作為靈感來源而開發的打字射擊遊戲,以 Visual Studio 2015 建立專案,基於 Game Framework 4.8 以 C++ 語言撰寫的 Windows 平台遊戲。

主選單

試玩影片

簡介

玩法

在本遊戲中有各種不同外貌的敵人(Enemy),但是他們都有一個共同點,那就是他們的身上都帶著一組英文單字。玩家必須在敵人接近之前,輸入其身上的單字,才能成功的把的敵人消滅。


規則

本遊戲除了普通的敵人外,另外還有多種強大的Boss,除了字數較長外,還各自擁有不同的技能。

特殊功能

當身邊有太多敵人即將接近,而且來不及將他們消滅的時候,玩家在每場遊戲,有三次的機會可以使用技能。按下"Enter"釋放出電磁脈衝(EMP),快速的將身邊的敵人消滅。

圖形/美術

我們遊戲圖形是以8 BITS復古風作為設計風格,它最大的特色就是在圖形的邊緣,看起來會有明顯的鋸齒。為了達成像素畫(Pixel Art) 的美術風格,大部分的圖形我們都是利用小畫家來繪製。和一般的繪圖軟體相比,小畫家非常適合來製作點陣圖,因為他可以很精確的在每一格中填色,並且可以很輕易的輸出Bmp格式的圖片,正好符合我們的需求。

說明畫面







角色選擇



遊戲畫面

輸入怪物身上對應的單字攻擊怪物
BOSS怪物會發射小怪攻擊玩家
按下空白鍵可以釋放EMP擊退怪物
通過關卡
Game Over

其他介面

暫停視窗
最高紀錄
遊玩記錄
清除遊玩記錄
關於頁面

遊戲下載

TypingTyping.zip (7.7mb, Windows only)

+ + + + \ No newline at end of file diff --git a/project/versatile-npm.html b/project/versatile-npm.html new file mode 100644 index 00000000..417a5837 --- /dev/null +++ b/project/versatile-npm.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Versatile Npm | ngseke + +

Versatile Npm

自訂 Npm 安裝指令瀏覽器擴充功能

  • Vue
  • TypeScript
  • Vuetify
  • npm
  • Nov, 2023

簡介

Versatile Npm 是 Google Chrome 擴充功能,使用 Vue 3 + TypeScript + Chrome Extension API + Vuetify 建構。

Demonstration

這個擴充功能可以讓使用者在 npm 套件頁面的右側欄加入 yarnpnpm 等安裝指令,方便開發者一鍵複製。

Screenshot 2

另外也提供設定 UI 供使用者自定義安裝指令,執行新增、刪除、編輯和排序操作。

Screenshot 1

這款 App 的 Logo 是透過 DALL·E 3 產生的,輸入 prompt 基本上包含 穿著帽 T 的水豚包裹紅色badge stylesport mascot style,最後透過 macOS 內建的「移除背景」功能去背。這過程幾乎不費吹灰之力,完全無需任何專業繪圖軟體。

其他候選 Logo 如下:
Logo Candidates

Demo


+ + + + \ No newline at end of file diff --git a/projects.html b/projects.html new file mode 100644 index 00000000..43f74522 --- /dev/null +++ b/projects.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + + Projects | ngseke + +

Browser Extension

  • Taiwan Company Blocker

    台灣求職網封鎖神器

  • Versatile Npm

    自訂 Npm 安裝指令瀏覽器擴充功能

  • LeetCode Night

    LeetCode 深色模式瀏覽器擴充功能

Web

  • iPhone Price

    台灣 iPhone 價格歷史趨勢

  • Koasu

    KOASÛ 白話字台語歌詞網誌

  • MCIP Official Website

    《樂台計畫》官方網站

  • MCIP CMS

    《樂台計畫》後台管理系統

  • Sinopac Dual Currency Card Calc.

    永豐幣倍卡回饋計算機

  • EM Optimization Lab

    《電磁最佳化實驗室》網站

System Design

  • BOSS: Beverage Online Shop System

    線上飲料購物系統

Game

  • Flip Card

    Emoji 翻牌配對遊戲

  • Gomoku

    線上五子棋對戰

  • Tic Tac Toe

    圈圈叉叉亂鬥

  • Raise Your Red Flag

    以 Webcam 重現經典團康遊戲《紅旗舉起來》

  • Typing Typing!

    8-bit 復古風格打字遊戲

Identity Design

  • NTUT-CSIE & NTUB-ACC Camp, 2017

    迎新《會炒不加辣,果資不加糖》品牌識別設計

  • Shanlinliang

    虛構涼扇品牌廣告《扇林涼》

+ + + + \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 00000000..fc1febf1 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://ngseke.me/sitemap.xml \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..658b745e --- /dev/null +++ b/sitemap.xml @@ -0,0 +1 @@ +https://ngseke.me/about2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/fe2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/projects2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/bootstrap-in-nuxt2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/check-if-key-exists2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/code-review-taxi-number2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/component-naming2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/customize-youtube-caption-font2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/react-handler-type2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/reproduce-bootstrap-grid-in-tailwind2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/typescript-as-const2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/vite-vue-ts-eslint-setup2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/webpack-config-esm2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/blog/wikipedia-link-converter2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/boss2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/camp-20172023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/credit-card-calc2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/em-optimization-lab2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/flag2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/gomoku2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/iphone-price2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/koasu2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/leetcode-night2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/mcip-cms2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/mcip2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/raise-your-red-flag2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/shanlinliang2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/tic-tac-toe2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/typing-typing2023-11-29T15:46:31.680Zdaily1.0https://ngseke.me/project/versatile-npm2023-11-29T15:46:31.680Zdaily1.0 \ No newline at end of file diff --git a/ssr-manifest.json b/ssr-manifest.json new file mode 100644 index 00000000..a4a80efa --- /dev/null +++ b/ssr-manifest.json @@ -0,0 +1,2633 @@ +{ + "\u0000/home/runner/work/ngseke.me/ngseke.me/node_modules/.pnpm/dayjs@1.11.5/node_modules/dayjs/dayjs.min.js?commonjs-module": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000/home/runner/work/ngseke.me/ngseke.me/node_modules/.pnpm/nprogress@0.2.0/node_modules/nprogress/nprogress.js?commonjs-module": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000/home/runner/work/ngseke.me/ngseke.me/node_modules/.pnpm/performance-now@2.1.0/node_modules/performance-now/lib/performance-now.js?commonjs-module": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000/home/runner/work/ngseke.me/ngseke.me/node_modules/.pnpm/raf@3.4.1/node_modules/raf/index.js?commonjs-module": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000/home/runner/work/ngseke.me/ngseke.me/node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/ease.js?commonjs-exports": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000/home/runner/work/ngseke.me/ngseke.me/node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/emitter.js?commonjs-module": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000commonjsHelpers.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000plugin-vue:export-helper": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000vite/modulepreload-polyfill": [ + "/assets/cover-background-f6446981.jpg" + ], + "\u0000vite/preload-helper": [ + "/assets/cover-background-f6446981.jpg" + ], + "../../../../../@vite-plugin-pages/generated-pages?id=~pages": [ + "/assets/cover-background-f6446981.jpg" + ], + "../../../../../@vite-plugin-pages/route-block": [ + "/assets/cover-background-f6446981.jpg" + ], + "index.html": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@fortawesome+fontawesome-svg-core@6.2.0/node_modules/@fortawesome/fontawesome-svg-core/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@fortawesome+free-brands-svg-icons@6.2.0/node_modules/@fortawesome/free-brands-svg-icons/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@fortawesome+free-solid-svg-icons@6.2.0/node_modules/@fortawesome/free-solid-svg-icons/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@fortawesome+vue-fontawesome@3.0.1_@fortawesome+fontawesome-svg-core@6.2.0_vue@3.2.39/node_modules/@fortawesome/vue-fontawesome/index.es.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@unhead+dom@1.7.4/node_modules/@unhead/dom/dist/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@unhead+shared@1.7.4/node_modules/@unhead/shared/dist/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@unhead+vue@1.7.4_vue@3.2.39/node_modules/@unhead/vue/dist/shared/vue.cf295fb1.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@unhead+vue@1.7.4_vue@3.2.39/node_modules/@unhead/vue/dist/shared/vue.f36acd1f.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vue+reactivity@3.2.39/node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vue+runtime-core@3.2.39/node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vue+runtime-dom@3.2.39/node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vue+shared@3.2.39/node_modules/@vue/shared/dist/shared.esm-bundler.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vueuse+core@9.13.0_vue@3.2.39/node_modules/@vueuse/core/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vueuse+integrations@9.13.0_axios@1.4.0_focus-trap@7.3.1_nprogress@0.2.0_vue@3.2.39/node_modules/@vueuse/integrations/useFocusTrap.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/@vueuse+shared@9.13.0_vue@3.2.39/node_modules/@vueuse/shared/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/dayjs@1.11.5/node_modules/dayjs/dayjs.min.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/focus-trap@7.3.1/node_modules/focus-trap/dist/focus-trap.esm.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/hookable@5.5.3/node_modules/hookable/dist/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/medium-zoom@1.0.6/node_modules/medium-zoom/dist/medium-zoom.esm.js": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "node_modules/.pnpm/nanoid@3.3.6/node_modules/nanoid/index.browser.js": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "node_modules/.pnpm/nanoid@4.0.2/node_modules/nanoid/index.browser.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/nprogress@0.2.0/node_modules/nprogress/nprogress.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/performance-now@2.1.0/node_modules/performance-now/lib/performance-now.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/raf@3.4.1/node_modules/raf/index.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/ease.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/emitter.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/index.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/scroll-to.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/scroll-to-element@2.0.3/node_modules/scroll-to-element/tween.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/tabbable@6.1.1/node_modules/tabbable/dist/index.esm.js": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/unhead@1.7.4/node_modules/unhead/dist/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/url-join@5.0.0/node_modules/url-join/lib/url-join.js": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "node_modules/.pnpm/vite-ssg@0.23.3_critters@0.0.20_vite@4.4.11_vue-router@4.1.5_vue@3.2.39/node_modules/vite-ssg/dist/index.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/vite-ssg@0.23.3_critters@0.0.20_vite@4.4.11_vue-router@4.1.5_vue@3.2.39/node_modules/vite-ssg/dist/shared/vite-ssg.5912142e.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/vite-ssg@0.23.3_critters@0.0.20_vite@4.4.11_vue-router@4.1.5_vue@3.2.39/node_modules/vite-ssg/dist/shared/vite-ssg.a009fbf1.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/vue-router@4.1.5_vue@3.2.39/node_modules/vue-router/dist/vue-router.mjs": [ + "/assets/cover-background-f6446981.jpg" + ], + "node_modules/.pnpm/vue-wrap-balancer@1.1.3_vue@3.2.39/node_modules/vue-wrap-balancer/dist/index.mjs": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/App.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/assets/img/cover-background.jpg": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/assets/img/post/check-if-key-exists/valueOf.png": [ + "/assets/check-if-key-exists-ce0584a7.js", + "/assets/valueOf-956b83dd.png" + ], + "src/assets/img/post/code-review-taxi-number/code-comments-be-like.jpg": [ + "/assets/code-review-taxi-number-a7cd2111.js", + "/assets/code-comments-be-like-59cec7d1.jpg" + ], + "src/assets/img/post/component-naming/go-to-file.png": [ + "/assets/component-naming-87122709.js", + "/assets/go-to-file-32915399.png", + "/assets/tabs-fc5ce7c7.png", + "/assets/open-editors-844d14f7.png" + ], + "src/assets/img/post/component-naming/open-editors.png": [ + "/assets/component-naming-87122709.js", + "/assets/go-to-file-32915399.png", + "/assets/tabs-fc5ce7c7.png", + "/assets/open-editors-844d14f7.png" + ], + "src/assets/img/post/component-naming/tabs.png": [ + "/assets/component-naming-87122709.js", + "/assets/go-to-file-32915399.png", + "/assets/tabs-fc5ce7c7.png", + "/assets/open-editors-844d14f7.png" + ], + "src/assets/img/post/customize-youtube-caption-font/after.jpg": [ + "/assets/customize-youtube-caption-font-3e0540da.js", + "/assets/youtube-cc-menu-f1682830.png", + "/assets/before-485cea7e.jpg", + "/assets/after-8a80ee82.jpg" + ], + "src/assets/img/post/customize-youtube-caption-font/before.jpg": [ + "/assets/customize-youtube-caption-font-3e0540da.js", + "/assets/youtube-cc-menu-f1682830.png", + "/assets/before-485cea7e.jpg", + "/assets/after-8a80ee82.jpg" + ], + "src/assets/img/post/customize-youtube-caption-font/youtube-cc-menu.png": [ + "/assets/customize-youtube-caption-font-3e0540da.js", + "/assets/youtube-cc-menu-f1682830.png", + "/assets/before-485cea7e.jpg", + "/assets/after-8a80ee82.jpg" + ], + "src/assets/img/post/react-handler-type/hover-type.png": [ + "/assets/react-handler-type-3a8ff22d.js", + "/assets/hover-type-d5f8cd06.png" + ], + "src/assets/img/post/typescript-as-const/intellisense.png": [ + "/assets/typescript-as-const-fb61f8e3.js", + "/assets/intellisense-e7791f21.png", + "/assets/rename-symbol-ac29a4c0.png" + ], + "src/assets/img/post/typescript-as-const/rename-symbol.png": [ + "/assets/typescript-as-const-fb61f8e3.js", + "/assets/intellisense-e7791f21.png", + "/assets/rename-symbol-ac29a4c0.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/command-reload-window.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/eslint-automatically-fixable.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/move-object-properties.gif": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/output-parserOptions-project-error.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/output-parserOptions-project-without-error.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/terminal-lint.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/tsconfig-include.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/vscode-auto-fix-on-save.gif": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/vscode-linting-result.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/vite-vue-ts-eslint-setup/vue-file-error.png": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/assets/img/post/wikipedia-link-converter/after.png": [ + "/assets/wikipedia-link-converter-5743973b.js", + "/assets/before-7a436972.png", + "/assets/after-97d65a70.png" + ], + "src/assets/img/post/wikipedia-link-converter/before.png": [ + "/assets/wikipedia-link-converter-5743973b.js", + "/assets/before-7a436972.png", + "/assets/after-97d65a70.png" + ], + "src/assets/img/project/boss/boss1.png": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/assets/img/project/boss/boss2.png": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/assets/img/project/boss/boss3.png": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/assets/img/project/boss/boss4.png": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/assets/img/project/boss/boss5.png": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/assets/img/project/camp2017/bumu1.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/bumu2.jpg": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/dahezhao.jpg": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/datoutie1.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/datoutie2.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/datoutie3.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/datoutie4.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/datoutie_fb.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/fb_zhuanye.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/hengfu.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/icon.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/logo_en.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/logo_fb.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/mingpai1.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/mingpai2.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/0.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/1.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/2.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/3.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/4.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/5.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/6.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce/7.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce1.jpg": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/shouce2.jpg": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/tixing.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/tshirt1.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/camp2017/tshirt2.png": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/assets/img/project/credit-card-calc/cover.png": [ + "/assets/credit-card-calc-28bb5562.js", + "/assets/cover-76f3ee6d.png" + ], + "src/assets/img/project/emo/cover.png": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/assets/img/project/emo/login.png": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/assets/img/project/emo/manage-carousel.png": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/assets/img/project/emo/manage-member.png": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/assets/img/project/emo/manage-research.png": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/assets/img/project/emo/manage-thesis.png": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/assets/img/project/flag/cover.png": [ + "/assets/raise-your-red-flag-6df7832f.js", + "/assets/cover-c3bb2266.png" + ], + "src/assets/img/project/gomoku/cover.png": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/assets/img/project/gomoku/mobile.png": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/assets/img/project/gomoku/register.png": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/assets/img/project/gomoku/room.png": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/assets/img/project/gomoku/title.png": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/assets/img/project/iphone-price/1.png": [ + "/assets/iphone-price-c267c7d7.js", + "/assets/cover-37fabbf0.png", + "/assets/1-408d5807.png", + "/assets/2-c1925776.png", + "/assets/3-c180c5f6.png" + ], + "src/assets/img/project/iphone-price/2.png": [ + "/assets/iphone-price-c267c7d7.js", + "/assets/cover-37fabbf0.png", + "/assets/1-408d5807.png", + "/assets/2-c1925776.png", + "/assets/3-c180c5f6.png" + ], + "src/assets/img/project/iphone-price/3.png": [ + "/assets/iphone-price-c267c7d7.js", + "/assets/cover-37fabbf0.png", + "/assets/1-408d5807.png", + "/assets/2-c1925776.png", + "/assets/3-c180c5f6.png" + ], + "src/assets/img/project/iphone-price/cover.png": [ + "/assets/iphone-price-c267c7d7.js", + "/assets/cover-37fabbf0.png", + "/assets/1-408d5807.png", + "/assets/2-c1925776.png", + "/assets/3-c180c5f6.png" + ], + "src/assets/img/project/koasu/cover.png": [ + "/assets/koasu-5245e6bf.js", + "/assets/cover-c99f79a0.png" + ], + "src/assets/img/project/leetcode-night/1.png": [ + "/assets/leetcode-night-b75823ff.js", + "/assets/banner-63e3ec60.png", + "/assets/store-f420ca97.png", + "/assets/2-8417bfde.png", + "/assets/1-d295ad8e.png" + ], + "src/assets/img/project/leetcode-night/2.png": [ + "/assets/leetcode-night-b75823ff.js", + "/assets/banner-63e3ec60.png", + "/assets/store-f420ca97.png", + "/assets/2-8417bfde.png", + "/assets/1-d295ad8e.png" + ], + "src/assets/img/project/leetcode-night/banner.png": [ + "/assets/leetcode-night-b75823ff.js", + "/assets/banner-63e3ec60.png", + "/assets/store-f420ca97.png", + "/assets/2-8417bfde.png", + "/assets/1-d295ad8e.png" + ], + "src/assets/img/project/leetcode-night/store.png": [ + "/assets/leetcode-night-b75823ff.js", + "/assets/banner-63e3ec60.png", + "/assets/store-f420ca97.png", + "/assets/2-8417bfde.png", + "/assets/1-d295ad8e.png" + ], + "src/assets/img/project/mcip-cms/competition.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/config.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/dashboard.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/edit-form.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/fb-cover.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/forms.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/login.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip-cms/pug.png": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/assets/img/project/mcip/1.png": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/mcip/2.png": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/mcip/3.png": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/mcip/deployment.png": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/mcip/facebook-messenger.png": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/mcip/legacy.png": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/mcip/logo-animation.gif": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/assets/img/project/shanlinliang/cover.png": [ + "/assets/shanlinliang-9edbff22.js", + "/assets/cover-ca6a18cd.png", + "/assets/sll1-98c64e6a.png", + "/assets/sll4-b982d4df.png", + "/assets/sll5-a9b2ee83.png" + ], + "src/assets/img/project/shanlinliang/sll1.png": [ + "/assets/shanlinliang-9edbff22.js", + "/assets/cover-ca6a18cd.png", + "/assets/sll1-98c64e6a.png", + "/assets/sll4-b982d4df.png", + "/assets/sll5-a9b2ee83.png" + ], + "src/assets/img/project/shanlinliang/sll4.png": [ + "/assets/shanlinliang-9edbff22.js", + "/assets/cover-ca6a18cd.png", + "/assets/sll1-98c64e6a.png", + "/assets/sll4-b982d4df.png", + "/assets/sll5-a9b2ee83.png" + ], + "src/assets/img/project/shanlinliang/sll5.png": [ + "/assets/shanlinliang-9edbff22.js", + "/assets/cover-ca6a18cd.png", + "/assets/sll1-98c64e6a.png", + "/assets/sll4-b982d4df.png", + "/assets/sll5-a9b2ee83.png" + ], + "src/assets/img/project/tic-tac-toe/1.png": [ + "/assets/tic-tac-toe-dd64aee5.js", + "/assets/1-ce441fa9.png", + "/assets/2-6e0e5062.png" + ], + "src/assets/img/project/tic-tac-toe/2.png": [ + "/assets/tic-tac-toe-dd64aee5.js", + "/assets/1-ce441fa9.png", + "/assets/2-6e0e5062.png" + ], + "src/assets/img/project/typingtyping/char1.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/char2.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/char3.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/enemy1.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/enemy2.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/enemy3.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/game1.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/game2.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/game3.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/game4.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/game5.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction1.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction2.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction3.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction4.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction5.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction6.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/instruction7.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/interface1.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/interface2.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/interface3.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/interface4.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/interface5.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/typingtyping/menu.png": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/assets/img/project/versatile-npm/1400x560.png": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/assets/img/project/versatile-npm/demo.gif": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/assets/img/project/versatile-npm/logo-candidates.png": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/assets/img/project/versatile-npm/screenshot-1.png": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/assets/img/project/versatile-npm/screenshot-2.png": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/components/AboutMe.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionContactMe.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionExperience.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionLayout.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionPosts.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionProjects.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionSelfIntroduction.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AboutMeSectionSkills.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/AlertReadAllProject.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/projects-34cb3ecc.js" + ], + "src/components/Button.vue": [ + "/assets/projects-34cb3ecc.js" + ], + "src/components/ButtonDarkMode.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/ButtonHamburger.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/ButtonRotateIcon.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/CardProject.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/CardProjectImageGlow.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/CardProjectLinkOverlay.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/Cover.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/CoverBody.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/CoverBody.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/CoverBody.vue?vue&type=style&index=0&scoped=b1cdf682&lang.scss": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/Emphasis.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/IsomorphicLink.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/Link.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/LinkIcon.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/LinkIconDemo.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/LinkIconGithub.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/LinkPost.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/LinkViewMore.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/NavbarHamburger.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/NavbarLink.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/NavbarLogo.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/OgImageTemplate.vue": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/OgImageTemplate.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/OgImageTemplate.vue?vue&type=style&index=0&scoped=7d4dce4c&lang.scss": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/PostLayout.vue": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/PostList.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/PostTags.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/blog-f75762ab.js" + ], + "src/components/ProjectList.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/ProjectTags.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/Prose.vue": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/Prose.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/Prose.vue?vue&type=style&index=0&scoped=eb8991d3&lang.scss": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/components/TeamMembers.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/project-e24afe95.js" + ], + "src/components/TheFooter.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/TheNavbar.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/components/TitleCategory.vue": [ + "/assets/projects-34cb3ecc.js" + ], + "src/components/TransitionUniversal.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useDark.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useDownplayProjects.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useHamburger.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useIsCoverVisible.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useIsScrolledToTop.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useMediumZoom.ts": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/composables/useMountedBodyClass.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useNProgress.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useOgImage.ts": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/composables/usePostFrontmatter.ts": [ + "/assets/blog-f75762ab.js" + ], + "src/composables/usePosts.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useProjectFrontmatter.ts": [ + "/assets/project-e24afe95.js" + ], + "src/composables/useProjects.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useReadHistory.ts": [ + "/assets/useReadHistory-d98c4b5d.js" + ], + "src/composables/useRootViewTransition.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/composables/useScrollToAnchor.ts": [ + "/assets/useOgImage-203f60bd.js", + "/assets/useOgImage-588433c9.css" + ], + "src/index.sass": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/main.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/modules/about-me-section.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/modules/constants.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/modules/date.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/modules/links.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/pages/404.vue": [ + "/assets/404-c7f247d5.js" + ], + "src/pages/[...404].vue": [ + "/assets/_...404_-c7f247d5.js" + ], + "src/pages/about.vue": [ + "/assets/about-c7f247d5.js" + ], + "src/pages/blog.vue": [ + "/assets/blog-f75762ab.js" + ], + "src/pages/blog.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/blog-f75762ab.js" + ], + "src/pages/blog/bootstrap-in-nuxt.md": [ + "/assets/bootstrap-in-nuxt-1bb6f56c.js" + ], + "src/pages/blog/bootstrap-in-nuxt.md?vue&type=script&setup=true&lang.ts": [ + "/assets/bootstrap-in-nuxt-1bb6f56c.js" + ], + "src/pages/blog/check-if-key-exists.md": [ + "/assets/check-if-key-exists-ce0584a7.js", + "/assets/valueOf-956b83dd.png" + ], + "src/pages/blog/check-if-key-exists.md?vue&type=script&setup=true&lang.ts": [ + "/assets/check-if-key-exists-ce0584a7.js", + "/assets/valueOf-956b83dd.png" + ], + "src/pages/blog/code-review-taxi-number.md": [ + "/assets/code-review-taxi-number-a7cd2111.js", + "/assets/code-comments-be-like-59cec7d1.jpg" + ], + "src/pages/blog/code-review-taxi-number.md?vue&type=script&setup=true&lang.ts": [ + "/assets/code-review-taxi-number-a7cd2111.js", + "/assets/code-comments-be-like-59cec7d1.jpg" + ], + "src/pages/blog/component-naming.md": [ + "/assets/component-naming-87122709.js", + "/assets/go-to-file-32915399.png", + "/assets/tabs-fc5ce7c7.png", + "/assets/open-editors-844d14f7.png" + ], + "src/pages/blog/component-naming.md?vue&type=script&setup=true&lang.ts": [ + "/assets/component-naming-87122709.js", + "/assets/go-to-file-32915399.png", + "/assets/tabs-fc5ce7c7.png", + "/assets/open-editors-844d14f7.png" + ], + "src/pages/blog/customize-youtube-caption-font.md": [ + "/assets/customize-youtube-caption-font-3e0540da.js", + "/assets/youtube-cc-menu-f1682830.png", + "/assets/before-485cea7e.jpg", + "/assets/after-8a80ee82.jpg" + ], + "src/pages/blog/customize-youtube-caption-font.md?vue&type=script&setup=true&lang.ts": [ + "/assets/customize-youtube-caption-font-3e0540da.js", + "/assets/youtube-cc-menu-f1682830.png", + "/assets/before-485cea7e.jpg", + "/assets/after-8a80ee82.jpg" + ], + "src/pages/blog/index.vue": [ + "/assets/index-4cd242c9.js" + ], + "src/pages/blog/index.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/index-4cd242c9.js" + ], + "src/pages/blog/react-handler-type.md": [ + "/assets/react-handler-type-3a8ff22d.js", + "/assets/hover-type-d5f8cd06.png" + ], + "src/pages/blog/react-handler-type.md?vue&type=script&setup=true&lang.ts": [ + "/assets/react-handler-type-3a8ff22d.js", + "/assets/hover-type-d5f8cd06.png" + ], + "src/pages/blog/reproduce-bootstrap-grid-in-tailwind.md": [ + "/assets/reproduce-bootstrap-grid-in-tailwind-ce693ef5.js" + ], + "src/pages/blog/reproduce-bootstrap-grid-in-tailwind.md?vue&type=script&setup=true&lang.ts": [ + "/assets/reproduce-bootstrap-grid-in-tailwind-ce693ef5.js" + ], + "src/pages/blog/typescript-as-const.md": [ + "/assets/typescript-as-const-fb61f8e3.js", + "/assets/intellisense-e7791f21.png", + "/assets/rename-symbol-ac29a4c0.png" + ], + "src/pages/blog/typescript-as-const.md?vue&type=script&setup=true&lang.ts": [ + "/assets/typescript-as-const-fb61f8e3.js", + "/assets/intellisense-e7791f21.png", + "/assets/rename-symbol-ac29a4c0.png" + ], + "src/pages/blog/vite-vue-ts-eslint-setup.md": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/pages/blog/vite-vue-ts-eslint-setup.md?vue&type=script&setup=true&lang.ts": [ + "/assets/vite-vue-ts-eslint-setup-1bf5e98d.js", + "/assets/output-parserOptions-project-error-ceeaa787.png", + "/assets/output-parserOptions-project-without-error-74ff031b.png", + "/assets/tsconfig-include-a4dd9125.png", + "/assets/command-reload-window-955490ff.png", + "/assets/vscode-linting-result-f315bd6a.png", + "/assets/vue-file-error-73be3796.png", + "/assets/move-object-properties-99f2f466.gif", + "/assets/vscode-auto-fix-on-save-445e3f9d.gif", + "/assets/terminal-lint-09df797c.png", + "/assets/eslint-automatically-fixable-4b63f152.png" + ], + "src/pages/blog/webpack-config-esm.md": [ + "/assets/webpack-config-esm-320be40c.js" + ], + "src/pages/blog/webpack-config-esm.md?vue&type=script&setup=true&lang.ts": [ + "/assets/webpack-config-esm-320be40c.js" + ], + "src/pages/blog/wikipedia-link-converter.md": [ + "/assets/wikipedia-link-converter-5743973b.js", + "/assets/before-7a436972.png", + "/assets/after-97d65a70.png" + ], + "src/pages/blog/wikipedia-link-converter.md?vue&type=script&setup=true&lang.ts": [ + "/assets/wikipedia-link-converter-5743973b.js", + "/assets/before-7a436972.png", + "/assets/after-97d65a70.png" + ], + "src/pages/fe.vue": [ + "/assets/fe-e736b23f.js" + ], + "src/pages/fe.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/fe-e736b23f.js" + ], + "src/pages/index.vue": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/pages/index.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/cover-background-f6446981.jpg" + ], + "src/pages/project.vue": [ + "/assets/project-e24afe95.js" + ], + "src/pages/project.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/project-e24afe95.js" + ], + "src/pages/project/boss.md": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/pages/project/boss.md?vue&type=script&setup=true&lang.ts": [ + "/assets/boss-7d1971fc.js", + "/assets/boss1-d440988b.png", + "/assets/boss2-3442c269.png", + "/assets/boss3-5ed82877.png", + "/assets/boss4-2c92bcf2.png", + "/assets/boss5-c63dc0c0.png" + ], + "src/pages/project/camp-2017.md": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/pages/project/camp-2017.md?vue&type=script&setup=true&lang.ts": [ + "/assets/camp-2017-d632b91c.js", + "/assets/logo_fb-3a5fbf8a.png", + "/assets/icon-3f390287.png", + "/assets/bumu1-2902b5f3.png", + "/assets/logo_en-8478642b.png", + "/assets/bumu2-9fbc6c06.jpg", + "/assets/mingpai1-50edfb95.png", + "/assets/mingpai2-dead019a.png", + "/assets/tshirt1-4f8fb94c.png", + "/assets/tshirt2-c61c550c.png", + "/assets/hengfu-324e5744.png", + "/assets/fb_zhuanye-8a3106ac.png", + "/assets/tixing-f836dc82.png", + "/assets/dahezhao-4534134f.jpg", + "/assets/datoutie1-f730def3.png", + "/assets/datoutie2-b626c0ae.png", + "/assets/datoutie3-99933bc0.png", + "/assets/datoutie4-8d7f399d.png", + "/assets/datoutie_fb-b37840ad.png", + "/assets/0-11090461.png", + "/assets/1-f5f11eb3.png", + "/assets/2-a067597c.png", + "/assets/3-78ab6d35.png", + "/assets/4-708e3caa.png", + "/assets/5-0224294b.png", + "/assets/6-a159bc1d.png", + "/assets/7-cb414675.png", + "/assets/shouce1-193ed0a8.jpg", + "/assets/shouce2-0a3f097e.jpg" + ], + "src/pages/project/camp2017.vue": [ + "/assets/camp2017-8a21aa39.js" + ], + "src/pages/project/credit-card-calc.md": [ + "/assets/credit-card-calc-28bb5562.js", + "/assets/cover-76f3ee6d.png" + ], + "src/pages/project/credit-card-calc.md?vue&type=script&setup=true&lang.ts": [ + "/assets/credit-card-calc-28bb5562.js", + "/assets/cover-76f3ee6d.png" + ], + "src/pages/project/em-optimization-lab.md": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/pages/project/em-optimization-lab.md?vue&type=script&setup=true&lang.ts": [ + "/assets/em-optimization-lab-d89b3c86.js", + "/assets/cover-cf0ba68f.png", + "/assets/login-7406d8e1.png", + "/assets/manage-thesis-1289eceb.png", + "/assets/manage-research-b7b5b46f.png", + "/assets/manage-member-9cbfc70b.png", + "/assets/manage-carousel-5d0aae2a.png" + ], + "src/pages/project/emo.vue": [ + "/assets/emo-5557c288.js" + ], + "src/pages/project/flag.vue": [ + "/assets/flag-5557c288.js" + ], + "src/pages/project/gomoku.md": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/pages/project/gomoku.md?vue&type=script&setup=true&lang.ts": [ + "/assets/gomoku-e312c04b.js", + "/assets/register-0126b216.png", + "/assets/room-f8da92ed.png", + "/assets/cover-bdee11bd.png", + "/assets/mobile-bc89f109.png" + ], + "src/pages/project/index.vue": [ + "/assets/index-95e78139.js" + ], + "src/pages/project/iphone-price.md": [ + "/assets/iphone-price-c267c7d7.js", + "/assets/cover-37fabbf0.png", + "/assets/1-408d5807.png", + "/assets/2-c1925776.png", + "/assets/3-c180c5f6.png" + ], + "src/pages/project/iphone-price.md?vue&type=script&setup=true&lang.ts": [ + "/assets/iphone-price-c267c7d7.js", + "/assets/cover-37fabbf0.png", + "/assets/1-408d5807.png", + "/assets/2-c1925776.png", + "/assets/3-c180c5f6.png" + ], + "src/pages/project/koasu.md": [ + "/assets/koasu-5245e6bf.js", + "/assets/cover-c99f79a0.png" + ], + "src/pages/project/koasu.md?vue&type=script&setup=true&lang.ts": [ + "/assets/koasu-5245e6bf.js", + "/assets/cover-c99f79a0.png" + ], + "src/pages/project/leetcode-night.md": [ + "/assets/leetcode-night-b75823ff.js", + "/assets/banner-63e3ec60.png", + "/assets/store-f420ca97.png", + "/assets/2-8417bfde.png", + "/assets/1-d295ad8e.png" + ], + "src/pages/project/leetcode-night.md?vue&type=script&setup=true&lang.ts": [ + "/assets/leetcode-night-b75823ff.js", + "/assets/banner-63e3ec60.png", + "/assets/store-f420ca97.png", + "/assets/2-8417bfde.png", + "/assets/1-d295ad8e.png" + ], + "src/pages/project/mcip-cms.md": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/pages/project/mcip-cms.md?vue&type=script&setup=true&lang.ts": [ + "/assets/mcip-cms-dcfd5ebb.js", + "/assets/fb-cover-6fb81625.png", + "/assets/dashboard-cc7d8fff.png", + "/assets/forms-d0d210e4.png", + "/assets/edit-form-5c88d403.png", + "/assets/login-3ac1d3a4.png", + "/assets/pug-67d261e0.png", + "/assets/competition-1f600c5f.png", + "/assets/config-21adacbc.png" + ], + "src/pages/project/mcip.md": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/pages/project/mcip.md?vue&type=script&setup=true&lang.ts": [ + "/assets/mcip-28b770e5.js", + "/assets/1-c36f7e53.png", + "/assets/2-4f25068f.png", + "/assets/3-ff7def49.png", + "/assets/legacy-42002bd1.png", + "/assets/facebook-messenger-bf65dd89.png", + "/assets/deployment-f152146e.png", + "/assets/logo-animation-3865b365.gif" + ], + "src/pages/project/raise-your-red-flag.md": [ + "/assets/raise-your-red-flag-6df7832f.js", + "/assets/cover-c3bb2266.png" + ], + "src/pages/project/raise-your-red-flag.md?vue&type=script&setup=true&lang.ts": [ + "/assets/raise-your-red-flag-6df7832f.js", + "/assets/cover-c3bb2266.png" + ], + "src/pages/project/shanlinliang.md": [ + "/assets/shanlinliang-9edbff22.js", + "/assets/cover-ca6a18cd.png", + "/assets/sll1-98c64e6a.png", + "/assets/sll4-b982d4df.png", + "/assets/sll5-a9b2ee83.png" + ], + "src/pages/project/shanlinliang.md?vue&type=script&setup=true&lang.ts": [ + "/assets/shanlinliang-9edbff22.js", + "/assets/cover-ca6a18cd.png", + "/assets/sll1-98c64e6a.png", + "/assets/sll4-b982d4df.png", + "/assets/sll5-a9b2ee83.png" + ], + "src/pages/project/tic-tac-toe.md": [ + "/assets/tic-tac-toe-dd64aee5.js", + "/assets/1-ce441fa9.png", + "/assets/2-6e0e5062.png" + ], + "src/pages/project/tic-tac-toe.md?vue&type=script&setup=true&lang.ts": [ + "/assets/tic-tac-toe-dd64aee5.js", + "/assets/1-ce441fa9.png", + "/assets/2-6e0e5062.png" + ], + "src/pages/project/typing-typing.md": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/pages/project/typing-typing.md?vue&type=script&setup=true&lang.ts": [ + "/assets/typing-typing-876fd270.js", + "/assets/menu-d3272083.png", + "/assets/char2-9248d6c0.png", + "/assets/char3-5ba16f0d.png", + "/assets/game1-d689a75e.png", + "/assets/game2-ec9b0a6e.png", + "/assets/game3-ae563b09.png", + "/assets/game4-ad0db896.png", + "/assets/interface3-2a7d9ac4.png", + "/assets/interface4-6491aa95.png", + "/assets/interface5-fd8930fa.png" + ], + "src/pages/project/typingtyping.vue": [ + "/assets/typingtyping-b132256f.js" + ], + "src/pages/project/versatile-npm.md": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/pages/project/versatile-npm.md?vue&type=script&setup=true&lang.ts": [ + "/assets/versatile-npm-d3f5c477.js", + "/assets/1400x560-02b6cc6e.png", + "/assets/demo-222ae85f.gif", + "/assets/screenshot-2-9726f1a7.png", + "/assets/screenshot-1-81b8fb35.png", + "/assets/logo-candidates-4f73c7bd.png" + ], + "src/pages/projects.vue": [ + "/assets/projects-34cb3ecc.js" + ], + "src/pages/projects.vue?vue&type=script&setup=true&lang.ts": [ + "/assets/projects-34cb3ecc.js" + ] +} \ No newline at end of file