From 3bd4114c63d855630e87f3439b4c3430d8f8a24f Mon Sep 17 00:00:00 2001 From: jaywcjlove <398188662@qq.com> Date: Sun, 20 Nov 2022 03:26:06 +0800 Subject: [PATCH] website: add search feature. #32 --- scripts/assets/search.svg | 3 + scripts/create.mjs | 6 +- scripts/index.mjs | 13 ++- scripts/js/fuse.min.js | 9 ++ scripts/js/main.js | 153 ++++++++++++++++++++++++++++++ scripts/nodes/footer.mjs | 4 +- scripts/nodes/header.mjs | 36 +++++++ scripts/nodes/search.mjs | 107 +++++++++++++++++++++ scripts/style/style.css | 173 ++++++++++++++++++++++++++++++++++ scripts/utils/anchorPoint.mjs | 49 ---------- scripts/utils/darkMode.mjs | 25 ----- scripts/watch.mjs | 22 ++++- 12 files changed, 516 insertions(+), 84 deletions(-) create mode 100644 scripts/assets/search.svg create mode 100644 scripts/js/fuse.min.js create mode 100644 scripts/js/main.js create mode 100644 scripts/nodes/search.mjs delete mode 100644 scripts/utils/anchorPoint.mjs diff --git a/scripts/assets/search.svg b/scripts/assets/search.svg new file mode 100644 index 0000000..149ca61 --- /dev/null +++ b/scripts/assets/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/scripts/create.mjs b/scripts/create.mjs index 4557257..8e53177 100644 --- a/scripts/create.mjs +++ b/scripts/create.mjs @@ -7,13 +7,13 @@ import { getCodeString } from 'rehype-rewrite'; import rehypeSlug from 'rehype-slug'; import { htmlTagAddAttri } from './nodes/htmlTagAddAttri.mjs'; import { footer } from './nodes/footer.mjs'; +import { search } from './nodes/search.mjs'; import { header } from './nodes/header.mjs'; import { rehypeUrls } from './utils/rehypeUrls.mjs'; import { tooltips } from './utils/tooltips.mjs'; import { homeCardIcons } from './utils/homeCardIcons.mjs'; import { getTocsTree, getTocsTitleNode, getTocsTitleNodeWarpper, addTocsInWarp } from './utils/getTocsTree.mjs'; import { rehypeTitle } from './utils/rehypeTitle.mjs'; -import { anchorPoint } from './utils/anchorPoint.mjs'; import { rehypePreviewHTML } from './utils/rehypePreviewHTML.mjs'; const favicon = `data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%221em%22%20width%3D%221em%22%3E%20%3Cpath%20d%3D%22m21.66%2010.44-.98%204.18c-.84%203.61-2.5%205.07-5.62%204.77-.5-.04-1.04-.13-1.62-.27l-1.68-.4c-4.17-.99-5.46-3.05-4.48-7.23l.98-4.19c.2-.85.44-1.59.74-2.2%201.17-2.42%203.16-3.07%206.5-2.28l1.67.39c4.19.98%205.47%203.05%204.49%207.23Z%22%20fill%3D%22%23c9d1d9%22%2F%3E%20%3Cpath%20d%3D%22M15.06%2019.39c-.62.42-1.4.77-2.35%201.08l-1.58.52c-3.97%201.28-6.06.21-7.35-3.76L2.5%2013.28c-1.28-3.97-.22-6.07%203.75-7.35l1.58-.52c.41-.13.8-.24%201.17-.31-.3.61-.54%201.35-.74%202.2l-.98%204.19c-.98%204.18.31%206.24%204.48%207.23l1.68.4c.58.14%201.12.23%201.62.27Zm2.43-8.88c-.06%200-.12-.01-.19-.02l-4.85-1.23a.75.75%200%200%201%20.37-1.45l4.85%201.23a.748.748%200%200%201-.18%201.47Z%22%20fill%3D%22%23228e6c%22%20%2F%3E%20%3Cpath%20d%3D%22M14.56%2013.89c-.06%200-.12-.01-.19-.02l-2.91-.74a.75.75%200%200%201%20.37-1.45l2.91.74c.4.1.64.51.54.91-.08.34-.38.56-.72.56Z%22%20fill%3D%22%23228e6c%22%20%2F%3E%20%3C%2Fsvg%3E`; @@ -92,6 +92,7 @@ export function create(str = '', options = {}) { if (!options.isHome) { const tocsMenus = getTocsTitleNode([...tocsData]); node.children = addTocsInWarp([...tocsData], getTocsTitleNodeWarpper(tocsMenus)); + // 生成搜索数据 tocsMenus.forEach((menu, idx) => { const level = menu?.properties['data-num']; if (idx + 1 === tocsMenus.length && level === 2) { @@ -110,7 +111,8 @@ export function create(str = '', options = {}) { } node.children.unshift(header(options)); node.children.push(footer(options)); - node.children.push(anchorPoint()); + // node.children.push(search(options)); + node.children = node.children.concat(search(options)); } } }, diff --git a/scripts/index.mjs b/scripts/index.mjs index dbb642a..ee00396 100644 --- a/scripts/index.mjs +++ b/scripts/index.mjs @@ -7,6 +7,7 @@ export const OUTOUT = path.resolve(process.cwd(), 'dist'); export const DOCS = path.resolve(process.cwd(), 'docs'); /** 搜索数据路径 */ export const SEARCH_DATA = path.resolve(OUTOUT, 'data.json'); +export const SEARCH_DATA_JS = path.resolve(OUTOUT, 'data.js'); export const SEARCH_DATA_CACHE = path.resolve(process.cwd(), 'node_modules/.cache/reference/data.json'); export async function createHTML(files = [], num = 0) { @@ -49,12 +50,21 @@ export async function createHTML(files = [], num = 0) { .map((name) => searchData[name]) .filter((item) => typeof item !== 'string'); await fs.writeJSON(SEARCH_DATA, resultSearchData); + await fs.writeFile(SEARCH_DATA_JS, `const REFS_DATA = ${JSON.stringify(resultSearchData)}`); } await fs.writeFile(outputHTMLPath, html); console.log(`♻️ \x1b[32;1m ${path.relative(OUTOUT, outputHTMLPath)} \x1b[0m`); createHTML(files, num); } +export async function copyCSSFile() { + await fs.copy(path.resolve(process.cwd(), 'scripts/style'), path.resolve(OUTOUT, 'style')); +} + +export async function copyJSFile() { + await fs.copy(path.resolve(process.cwd(), 'scripts/js'), path.resolve(OUTOUT, 'js')); +} + export async function run() { try { await fs.ensureDir(OUTOUT); @@ -63,7 +73,8 @@ export async function run() { await fs.ensureFile(SEARCH_DATA_CACHE); await fs.writeFile(SEARCH_DATA_CACHE, '{}'); await fs.writeFile(SEARCH_DATA, '[]'); - await fs.copy(path.resolve(process.cwd(), 'scripts/style'), path.resolve(OUTOUT, 'style')); + await copyCSSFile(); + await copyJSFile(); const files = await recursiveReaddirFiles(process.cwd(), { ignored: /\/(node_modules|\.git)/, exclude: /(\.json|\.mjs|CONTRIBUTING\.md)$/, diff --git a/scripts/js/fuse.min.js b/scripts/js/fuse.min.js new file mode 100644 index 0000000..455ad13 --- /dev/null +++ b/scripts/js/fuse.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v6.6.2 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2022 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:1,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:3,n=new Map,r=Math.pow(10,t);return{get:function(t){var i=t.match(C).length;if(n.has(i))return n.get(i);var o=1/Math.pow(i,.5*e),c=parseFloat(Math.round(o*r)/r);return n.set(i,c),c},clear:function(){n.clear()}}}var $=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.getFn,i=void 0===n?I.getFn:n,o=t.fieldNormWeight,c=void 0===o?I.fieldNormWeight:o;r(this,e),this.norm=E(c,3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return o(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,g(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();g(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?I.getFn:r,o=n.fieldNormWeight,c=void 0===o?I.fieldNormWeight:o,a=new $({getFn:i,fieldNormWeight:c});return a.setKeys(e.map(_)),a.setSources(t),a.create(),a}function R(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,o=void 0===i?0:i,c=t.expectedLocation,a=void 0===c?0:c,s=t.distance,u=void 0===s?I.distance:s,h=t.ignoreLocation,l=void 0===h?I.ignoreLocation:h,f=r/e.length;if(l)return f;var d=Math.abs(a-o);return u?f+d/u:d?1:f}function N(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:I.minMatchCharLength,n=[],r=-1,i=-1,o=0,c=e.length;o=t&&n.push([r,i]),r=-1)}return e[o-1]&&o-r>=t&&n.push([r,o-1]),n}var P=32;function W(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},o=i.location,c=void 0===o?I.location:o,a=i.threshold,s=void 0===a?I.threshold:a,u=i.distance,h=void 0===u?I.distance:u,l=i.includeMatches,f=void 0===l?I.includeMatches:l,d=i.findAllMatches,v=void 0===d?I.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?I.minMatchCharLength:g,p=i.isCaseSensitive,m=void 0===p?I.isCaseSensitive:p,k=i.ignoreLocation,M=void 0===k?I.ignoreLocation:k;if(r(this,e),this.options={location:c,threshold:s,distance:h,includeMatches:f,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:m,ignoreLocation:M},this.pattern=m?t:t.toLowerCase(),this.chunks=[],this.pattern.length){var b=function(e,t){n.chunks.push({pattern:e,alphabet:W(e),startIndex:t})},x=this.pattern.length;if(x>P){for(var w=0,L=x%P,S=x-L;w3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,o=void 0===i?I.location:i,c=r.distance,a=void 0===c?I.distance:c,s=r.threshold,u=void 0===s?I.threshold:s,h=r.findAllMatches,l=void 0===h?I.findAllMatches:h,f=r.minMatchCharLength,d=void 0===f?I.minMatchCharLength:f,v=r.includeMatches,g=void 0===v?I.includeMatches:v,y=r.ignoreLocation,p=void 0===y?I.ignoreLocation:y;if(t.length>P)throw new Error(w(P));for(var m,k=t.length,M=e.length,b=Math.max(0,Math.min(o,M)),x=u,L=b,S=d>1||g,_=S?Array(M):[];(m=e.indexOf(t,L))>-1;){var O=R(t,{currentLocation:m,expectedLocation:b,distance:a,ignoreLocation:p});if(x=Math.min(O,x),L=m+k,S)for(var j=0;j=z;q-=1){var B=q-1,J=n[e.charAt(B)];if(S&&(_[B]=+!!J),K[q]=(K[q+1]<<1|1)&J,F&&(K[q]|=(A[q+1]|A[q])<<1|1|A[q+1]),K[q]&$&&(C=R(t,{errors:F,currentLocation:B,expectedLocation:b,distance:a,ignoreLocation:p}))<=x){if(x=C,(L=B)<=b)break;z=Math.max(1,2*b-L)}}if(R(t,{errors:F+1,currentLocation:b,expectedLocation:b,distance:a,ignoreLocation:p})>x)break;A=K}var U={isMatch:L>=0,score:Math.max(.001,C)};if(S){var V=N(_,d);V.length?g&&(U.indices=V):U.isMatch=!1}return U}(e,n,i,{location:c+o,distance:a,threshold:s,findAllMatches:u,minMatchCharLength:h,includeMatches:r,ignoreLocation:l}),p=y.isMatch,m=y.score,k=y.indices;p&&(g=!0),v+=m,p&&k&&(d=[].concat(f(d),f(k)))}));var y={isMatch:g,score:g?v/this.chunks.length:1};return g&&r&&(y.indices=d),y}}]),e}(),z=function(){function e(t){r(this,e),this.pattern=t}return o(e,[{key:"search",value:function(){}}],[{key:"isMultiMatch",value:function(e){return D(e,this.multiRegex)}},{key:"isSingleMatch",value:function(e){return D(e,this.singleRegex)}}]),e}();function D(e,t){var n=e.match(t);return n?n[1]:null}var K=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"exact"}},{key:"multiRegex",get:function(){return/^="(.*)"$/}},{key:"singleRegex",get:function(){return/^=(.*)$/}}]),n}(z),q=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"$/}},{key:"singleRegex",get:function(){return/^!(.*)$/}}]),n}(z),B=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}}],[{key:"type",get:function(){return"prefix-exact"}},{key:"multiRegex",get:function(){return/^\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^\^(.*)$/}}]),n}(z),J=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-prefix-exact"}},{key:"multiRegex",get:function(){return/^!\^"(.*)"$/}},{key:"singleRegex",get:function(){return/^!\^(.*)$/}}]),n}(z),U=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}}],[{key:"type",get:function(){return"suffix-exact"}},{key:"multiRegex",get:function(){return/^"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^(.*)\$$/}}]),n}(z),V=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){var t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}}],[{key:"type",get:function(){return"inverse-suffix-exact"}},{key:"multiRegex",get:function(){return/^!"(.*)"\$$/}},{key:"singleRegex",get:function(){return/^!(.*)\$$/}}]),n}(z),G=function(e){a(n,e);var t=l(n);function n(e){var i,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=o.location,a=void 0===c?I.location:c,s=o.threshold,u=void 0===s?I.threshold:s,h=o.distance,l=void 0===h?I.distance:h,f=o.includeMatches,d=void 0===f?I.includeMatches:f,v=o.findAllMatches,g=void 0===v?I.findAllMatches:v,y=o.minMatchCharLength,p=void 0===y?I.minMatchCharLength:y,m=o.isCaseSensitive,k=void 0===m?I.isCaseSensitive:m,M=o.ignoreLocation,b=void 0===M?I.ignoreLocation:M;return r(this,n),(i=t.call(this,e))._bitapSearch=new T(e,{location:a,threshold:u,distance:l,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:k,ignoreLocation:b}),i}return o(n,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),n}(z),H=function(e){a(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return o(n,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var o=!!r.length;return{isMatch:o,score:o?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),n}(z),Q=[K,H,B,J,V,U,q,G],X=Q.length,Y=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/;function Z(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(Y).filter((function(e){return e&&!!e.trim()})),r=[],i=0,o=n.length;i1&&void 0!==arguments[1]?arguments[1]:{},i=n.isCaseSensitive,o=void 0===i?I.isCaseSensitive:i,c=n.includeMatches,a=void 0===c?I.includeMatches:c,s=n.minMatchCharLength,u=void 0===s?I.minMatchCharLength:s,h=n.ignoreLocation,l=void 0===h?I.ignoreLocation:h,f=n.findAllMatches,d=void 0===f?I.findAllMatches:f,v=n.location,g=void 0===v?I.location:v,y=n.threshold,p=void 0===y?I.threshold:y,m=n.distance,k=void 0===m?I.distance:m;r(this,e),this.query=null,this.options={isCaseSensitive:o,includeMatches:a,minMatchCharLength:u,findAllMatches:d,ignoreLocation:l,location:g,threshold:p,distance:k},this.pattern=o?t:t.toLowerCase(),this.query=Z(this.pattern,this.options)}return o(e,[{key:"searchIn",value:function(e){var t=this.query;if(!t)return{isMatch:!1,score:1};var n=this.options,r=n.includeMatches;e=n.isCaseSensitive?e:e.toLowerCase();for(var i=0,o=[],c=0,a=0,s=t.length;a-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function ve(e,t){t.score=e.score}function ge(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?I.includeMatches:r,o=n.includeScore,c=void 0===o?I.includeScore:o,a=[];return i&&a.push(de),c&&a.push(ve),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return a.length&&a.forEach((function(t){t(e,r)})),r}))}var ye=function(){function e(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=arguments.length>2?arguments[2]:void 0;r(this,e),this.options=t(t({},I),i),this.options.useExtendedSearch,this._keyStore=new S(this.options.keys),this.setCollection(n,o)}return o(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof $))throw new Error("Incorrect 'index' type");this._myIndex=t||F(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}},{key:"add",value:function(e){k(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.limit,r=void 0===n?-1:n,i=this.options,o=i.includeMatches,c=i.includeScore,a=i.shouldSort,s=i.sortFn,u=i.ignoreFieldNorm,h=g(e)?g(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return fe(h,{ignoreFieldNorm:u}),a&&h.sort(s),y(r)&&r>-1&&(h=h.slice(0,r)),ge(h,this._docs,{includeMatches:o,includeScore:c})}},{key:"_searchStringList",value:function(e){var t=re(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,o=e.n;if(k(n)){var c=t.searchIn(n),a=c.isMatch,s=c.score,u=c.indices;a&&r.push({item:n,idx:i,matches:[{score:s,value:n,norm:o,indices:u}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=function(e,t){var n=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).auto,r=void 0===n||n,i=function e(n){var i=Object.keys(n),o=ue(n);if(!o&&i.length>1&&!se(n))return e(le(n));if(he(n)){var c=o?n[ce]:i[0],a=o?n[ae]:n[c];if(!g(a))throw new Error(x(c));var s={keyId:j(c),pattern:a};return r&&(s.searcher=re(a,t)),s}var u={children:[],operator:i[0]};return i.forEach((function(t){var r=n[t];v(r)&&r.forEach((function(t){u.children.push(e(t))}))})),u};return se(e)||(e=le(e)),i(e)}(e,this.options),r=function e(n,r,i){if(!n.children){var o=n.keyId,c=n.searcher,a=t._findMatches({key:t._keyStore.get(o),value:t._myIndex.getValueForItemAtKeyId(r,o),searcher:c});return a&&a.length?[{idx:i,item:r,matches:a}]:[]}for(var s=[],u=0,h=n.children.length;u1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?I.getFn:n,i=t.fieldNormWeight,o=void 0===i?I.fieldNormWeight:i,c=e.keys,a=e.records,s=new $({getFn:r,fieldNormWeight:o});return s.setKeys(c),s.setIndexRecords(a),s},ye.config=I,function(){ne.push.apply(ne,arguments)}(te),ye},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Fuse=t(); diff --git a/scripts/js/main.js b/scripts/js/main.js new file mode 100644 index 0000000..c7109cc --- /dev/null +++ b/scripts/js/main.js @@ -0,0 +1,153 @@ +/** ==========dark theme============== */ +const LOCAL_NANE = '_dark_mode_theme_' +const rememberedValue = localStorage.getItem(LOCAL_NANE); +if (rememberedValue && ['light', 'dark'].includes(rememberedValue)) { + document.documentElement.setAttribute('data-color-mode', rememberedValue); +} +const button = document.querySelector('#darkMode'); +button.onclick = () => { + const theme = document.documentElement.dataset.colorMode; + const mode = theme === 'light' ? 'dark' : 'light'; + document.documentElement.setAttribute('data-color-mode', mode); + localStorage.setItem(LOCAL_NANE, mode); +} +/** ==========anchor============== */ +if(('onhashchange' in window) && ((typeof document.documentMode==='undefined') || document.documentMode==8)) { + window.onhashchange = function () { + anchorPoint() + updateAnchor() + }; +} +function anchorPoint() { + const hash = window.location.hash?.replace(/^#/, '') || ''; + const elm = document.getElementById(decodeURIComponent(hash)); + Array.from(document.querySelectorAll('.h2wrap-body .wrap')).forEach((elm) => elm.classList.remove('active')) + if (elm?.tagName === 'H3') { + elm?.parentElement?.parentElement?.classList.add('active'); + } +} +anchorPoint(); +function updateAnchor(element) { + const anchorContainer = document.querySelectorAll('.menu-tocs .menu-modal a.tocs-link'); + anchorContainer.forEach((tocanchor) => { + tocanchor.classList.remove('is-active-link'); + }); + const anchor = element || document.querySelector(`a.tocs-link[href='${decodeURIComponent(window.location.hash)}']`); + if (anchor) { + anchor.classList.add('is-active-link'); + } +} +// toc 定位 +updateAnchor() +const anchorAll = document.querySelectorAll('.menu-tocs .menu-modal a.tocs-link'); +anchorAll.forEach((item) => { + item.addEventListener('click', (e) => { + updateAnchor() + }) +}) + +/** ==========search============== */ +const fuse = new Fuse(REFS_DATA, { + includeScore: !1, + shouldSort: !0, + includeMatches: !0, + matchEmptyQuery: !0, + threshold: .1, + keys: [ + { name: "title", weight: 12 }, + { name: 'intro', weight: 2 }, + { name: 'sections.t', weight: 5 } + ], +}); + +const searchBtn = document.getElementById('searchbtn'); +const searchBox = document.getElementById('mysearch'); +const searchInput = document.getElementById('mysearch-input'); +const closeBtn = document.getElementById('mysearch-close'); +const searchMenu = document.getElementById('mysearch-menu'); +const searchContent = document.getElementById('mysearch-content'); +const isHome = document.body.classList.contains('home'); + +searchBtn.addEventListener('click', (ev) => { + ev.preventDefault(); + showSearch(); +}); + +closeBtn.addEventListener('click', hideSearch); +searchBox.addEventListener('click', hideSearch); +searchBox.firstChild.addEventListener('click', (ev) => ev.stopPropagation()); +searchInput.addEventListener('input', (evn) => searchResult(evn.target.value)); +document.addEventListener('keydown', (ev) => { + if (ev.metaKey && ev.key.toLocaleLowerCase() === 'k') { + searchBox.classList.contains('show') ? hideSearch() : showSearch(); + } +}); + +function showSearch() { + document.body.classList.add('search'); + searchBox.classList.add('show'); + searchInput.focus(); +} + +function hideSearch() { + document.body.classList.remove('search'); + searchBox.classList.remove('show'); +} +let result = [] +let inputValue = ''; + +function searchResult(value) { + inputValue = value; + result = fuse.search(value); + let menuHTML = ''; + result.forEach((item, idx) => { + const label = item.item.title.replace(new RegExp(value, 'ig'), (txt) => { + return `${txt}` + }) + const href = isHome ? item.item.path : item.item.path.replace('docs/', ''); + if (idx === 0) { + menuHTML += `${label}`; + } else { + menuHTML += `${label}`; + } + }); + searchMenu.innerHTML = menuHTML; + searchSectionsResult(); + const data = Array.from(searchMenu.children) + data.forEach((anchor, idx) => { + anchor.onmouseenter = (evn) => { + data.forEach(item => item.classList.remove('active')); + evn.target.classList.add('active'); + searchSectionsResult(idx); + } + }); + const anchorData = searchContent.querySelectorAll('a'); + Array.from(anchorData).forEach((item) => { + item.addEventListener('click', hideSearch); + }) +} + +function searchSectionsResult(idx = 0) { + const data = result[idx] || []; + const title = (data.item?.intro || '').replace(new RegExp(inputValue, 'ig'), (txt) => { + return `${txt}` + }); + let sectionHTML = `

${title}

    `; + if (data && data.item && data.item.sections) { + data.item.sections.forEach((item, idx) => { + const label = item.t.replace(new RegExp(inputValue, 'ig'), (txt) => { + return `${txt}` + }) + const href = isHome ? data.item.path : data.item.path.replace('docs/', ''); + if (item.l < 3) { + sectionHTML += `
  1. ${label}
    ` + } else { + sectionHTML += `${label}` + } + if (data.item.sections.length === idx + 1) { + sectionHTML += `
  2. ` + } + }) + } + searchContent.innerHTML = sectionHTML; +} \ No newline at end of file diff --git a/scripts/nodes/footer.mjs b/scripts/nodes/footer.mjs index 78df1ad..f3a454d 100644 --- a/scripts/nodes/footer.mjs +++ b/scripts/nodes/footer.mjs @@ -1,8 +1,8 @@ import formatter from '@uiw/formatter'; -export function footer(options = {}) { +export function footer({ isHome } = {}) { let footerText = '© 2022 Kenny Wang.'; - if (options.isHome) { + if (isHome) { const now = new Date(); const utc = now.getTime() + now.getTimezoneOffset() * 60000; const cst = new Date(utc + 3600000 * 8); diff --git a/scripts/nodes/header.mjs b/scripts/nodes/header.mjs index 2689f6f..4a040b5 100644 --- a/scripts/nodes/header.mjs +++ b/scripts/nodes/header.mjs @@ -4,9 +4,45 @@ import { getSVGNode } from '../utils/getSVGNode.mjs'; import { darkMode } from '../utils/darkMode.mjs'; const ICONS_PATH = path.resolve(process.cwd(), 'scripts/assets/quickreference.svg'); +const ICONS_SEARCH_PATH = path.resolve(process.cwd(), 'scripts/assets/search.svg'); + export function header({ homePath, githubURL = '' }) { const svgNode = getSVGNode(ICONS_PATH); + const svgSearchNode = getSVGNode(ICONS_SEARCH_PATH); const data = [ + { + menu: true, + href: 'javascript:void(0);', + class: ['searchbtn'], + id: 'searchbtn', + children: [ + ...svgSearchNode, + { + type: 'element', + tagName: 'span', + children: [ + { + type: 'text', + value: '搜索', + }, + ], + }, + { + type: 'element', + tagName: 'span', + children: [ + { + type: 'text', + value: '⌘', + }, + { + type: 'text', + value: 'K', + }, + ], + }, + ], + }, { menu: true, href: githubURL, diff --git a/scripts/nodes/search.mjs b/scripts/nodes/search.mjs new file mode 100644 index 0000000..0d8f2f2 --- /dev/null +++ b/scripts/nodes/search.mjs @@ -0,0 +1,107 @@ +import path from 'path'; +import { getSVGNode } from '../utils/getSVGNode.mjs'; + +const ICONS_SEARCH_PATH = path.resolve(process.cwd(), 'scripts/assets/search.svg'); + +export function search({ homePath = '', isHome } = {}) { + const relativePath = homePath.replace(/\/?index.html$/, isHome ? '' : '/'); + const fuseJSUrl = relativePath + 'js/fuse.min.js'; + const manJSUrl = relativePath + 'js/main.js'; + const dataJSUrl = relativePath + 'data.js'; + const svgSearchNode = getSVGNode(ICONS_SEARCH_PATH); + return [ + { + type: 'element', + tagName: 'script', + properties: { + src: dataJSUrl, + defer: true, + }, + }, + { + type: 'element', + tagName: 'script', + properties: { + src: fuseJSUrl, + defer: true, + }, + }, + { + type: 'element', + tagName: 'script', + properties: { + src: manJSUrl, + defer: true, + }, + }, + { + type: 'element', + tagName: 'div', + properties: { + id: 'mysearch', + }, + children: [ + { + type: 'element', + tagName: 'div', + properties: { + class: ['mysearch-box'], + }, + children: [ + { + type: 'element', + tagName: 'div', + properties: { class: ['mysearch-input'] }, + children: [ + { + type: 'element', + tagName: 'div', + properties: {}, + children: [ + ...svgSearchNode, + { + type: 'element', + tagName: 'input', + properties: { id: ['mysearch-input'], type: 'search' }, + children: [], + }, + { + type: 'element', + tagName: 'div', + properties: { class: ['mysearch-clear'] }, + }, + ], + }, + { + type: 'element', + tagName: 'button', + properties: { id: ['mysearch-close'], type: 'button' }, + children: [{ type: 'text', value: '取消' }], + }, + ], + }, + { + type: 'element', + tagName: 'div', + properties: { class: ['mysearch-result'] }, + children: [ + { + type: 'element', + tagName: 'div', + properties: { id: 'mysearch-menu' }, + children: [], + }, + { + type: 'element', + tagName: 'div', + properties: { id: 'mysearch-content' }, + children: [], + }, + ], + }, + ], + }, + ], + }, + ]; +} diff --git a/scripts/style/style.css b/scripts/style/style.css index cf3902e..b5d9e56 100644 --- a/scripts/style/style.css +++ b/scripts/style/style.css @@ -80,6 +80,7 @@ body { --color-attention-subtle: #fff8c5; --color-danger-fg: #cf222e; --box-shadow: 109 109 109; + --primary-color: #228e6c; } [data-color-mode*='dark'], @@ -128,6 +129,7 @@ body { --color-attention-subtle: rgba(187, 128, 9, 0.15); --color-danger-fg: #f85149; --box-shadow: 0 0 0; + --primary-color: #228e6c; } body { @@ -540,6 +542,21 @@ a.text-grey { .header-nav .menu a > span { font-size: 0.9rem; } +.header-nav .menu .searchbtn { + text-decoration-color: transparent; + gap: 0.75rem; +} +.header-nav .menu .searchbtn span:last-child { + transition: all 0.3s; + border: 1px solid var(--color-border-default); + border-radius: 3px; + padding: 1px 1px 1px 3px; + letter-spacing: 3px; +} +.header-nav .menu .searchbtn:hover span:last-child { + border-color: var(--primary-color); + color: var(--primary-color); +} .header-nav .menu button { font-family: inherit; font-size: 100%; @@ -1199,6 +1216,158 @@ body:not(.home) .h2wrap-body > .wrap:hover .h3wrap > h3 a::after { font-size: 0.75rem; } +body.search { + overflow: hidden; +} +#mysearch { + transition: all 0.3s; + display: none; +} +#mysearch.show .mysearch-box { + background-color: var(--color-canvas-default); + box-shadow: 0 0 #0000, 0 0 #0000, 0 0 #0000, 0 0 #0000, 0 35px 60px -15px rgba(0, 0, 0, 0.3); + border-radius: 0.5rem; + display: flex; + flex-direction: column; + max-width: 1024px; + width: 100%; + height: 100%; +} +#mysearch.show .mysearch-result > * { + width: 50%; + overflow-y: auto; + padding: 0.6rem; +} +#mysearch.show .mysearch-result > :last-child { + background-color: var(--color-neutral-muted); + border-bottom-right-radius: 0.5rem; +} +#mysearch.show .mysearch-result { + display: flex; + flex: 1; + height: calc(100% - 3.5rem); +} +#mysearch.show { + background-color: var(--color-neutral-muted); + height: 100vh; + left: 0; + position: fixed; + top: 0; + width: 100vw; + z-index: 200; + display: flex; + align-items: center; + justify-content: center; +} + +.mysearch-input { + height: 3.5rem; + display: flex; + flex-direction: row; + border-bottom: 1px solid var(--color-neutral-muted); +} +.mysearch-input > :first-child { + flex: 1; + display: flex; + flex-direction: row; + align-items: center; +} +.mysearch-input > :first-child svg { + margin-left: 1rem; + font-size: 1.3rem; + position: absolute; + color: var(--primary-color); +} +.mysearch-input > :first-child input { + flex: 1; + height: 100%; + padding-left: 2.9rem; + font-size: 1.6rem; + color: var(--color-fg-default); + border: 0; + font-weight: 800; + background: transparent; + outline: 0; +} +#mysearch-close:hover { + color: var(--primary-color); +} +#mysearch-close { + background-color: transparent; + color: var(--color-fg-default); + border: 0; + padding: 0 1.6rem; + cursor: pointer; + font-size: 1.1rem; + transition: all 0.3s; +} + +#mysearch-menu a + a { + margin: 0.2rem 0; +} +#mysearch-menu a { + display: flex; + padding-top: 0.625rem; + padding-bottom: 0.625rem; + padding-left: 0.875rem; + padding-right: 0.875rem; + transition: all 0.3s; + white-space: pre-wrap; + text-decoration: none; + color: var(--color-fg-default); +} +#mysearch-menu a:hover, +#mysearch-menu a.active { + background-color: var(--color-neutral-muted); + border-radius: 0.5rem; +} +#mysearch-content ol li div a:hover { + background-color: var(--primary-color); + color: #fff; +} +#mysearch-content ol li div a { + padding: 0.125rem 0.5rem; + border-radius: 100px; + margin: 0.1rem 0.2rem; + color: var(--color-fg-subtle); +} +#mysearch-content ol li div { + margin-left: -1.54rem; + padding-top: 0.82rem; +} +#mysearch-content ol li > a:hover { + text-decoration: underline; +} +#mysearch-content ol li > a { + font-weight: bold; +} +#mysearch-content ol li a { + font-size: 0.85rem; + white-space: nowrap; + display: inline-block; + text-decoration: none; + color: var(--color-fg-default); + transition: all 0.3s; +} +#mysearch-content ol li { + word-break: break-all; + white-space: pre-wrap; + padding-bottom: 1.56rem; +} +#mysearch-content ol { + list-style: auto; + padding-left: 1.75rem; +} +#mysearch-content h3 { + padding-bottom: 1.3rem; + text-align: center; + padding-top: 1.5rem; + color: var(--color-fg-subtle); + max-width: 23rem; + margin: 0 auto; + font-size: 0.85rem; +} + @media (min-width: 1024px) { .h2wrap-body { display: grid; @@ -1207,6 +1376,10 @@ body:not(.home) .h2wrap-body > .wrap:hover .h3wrap > h3 a::after { .h2wrap-body > .wrap { margin-bottom: 0; } + #mysearch.show { + padding-bottom: 5rem; + padding-top: 4rem; + } } @media (375px <= width <= 1024px) { .header-nav .title { diff --git a/scripts/utils/anchorPoint.mjs b/scripts/utils/anchorPoint.mjs deleted file mode 100644 index 53a2025..0000000 --- a/scripts/utils/anchorPoint.mjs +++ /dev/null @@ -1,49 +0,0 @@ -const scripts = ` -if(('onhashchange' in window) && ((typeof document.documentMode==='undefined') || document.documentMode==8)) { - window.onhashchange = function () { - anchorPoint() - updateAnchor() - }; -} -function anchorPoint() { - const hash = window.location.hash?.replace(/^#/, '') || ''; - const elm = document.getElementById(decodeURIComponent(hash)); - Array.from(document.querySelectorAll('.h2wrap-body .wrap')).forEach((elm) => elm.classList.remove('active')) - if (elm?.tagName === 'H3') { - elm?.parentElement?.parentElement?.classList.add('active'); - } -} -anchorPoint(); - -function updateAnchor(element) { - const anchorContainer = document.querySelectorAll('.menu-tocs .menu-modal a.tocs-link'); - anchorContainer.forEach((tocanchor) => { - tocanchor.classList.remove('is-active-link'); - }); - const anchor = element || document.querySelector(\`a.tocs-link[href='\${decodeURIComponent(window.location.hash)}']\`); - if (anchor) { - anchor.classList.add('is-active-link'); - } -} -// toc 定位 -updateAnchor() -const anchor = document.querySelectorAll('.menu-tocs .menu-modal a.tocs-link'); -anchor.forEach((item) => { - item.addEventListener('click', (e) => { - updateAnchor() - }) -}) -`; - -export function anchorPoint() { - return { - type: 'element', - tagName: 'script', - children: [ - { - type: 'text', - value: scripts, - }, - ], - }; -} diff --git a/scripts/utils/darkMode.mjs b/scripts/utils/darkMode.mjs index c55371f..6c2eb28 100644 --- a/scripts/utils/darkMode.mjs +++ b/scripts/utils/darkMode.mjs @@ -1,21 +1,6 @@ import path from 'path'; import { getSVGNode } from './getSVGNode.mjs'; -const scripts = ` - const LOCAL_NANE = '_dark_mode_theme_' - const rememberedValue = localStorage.getItem(LOCAL_NANE); - if (rememberedValue && ['light', 'dark'].includes(rememberedValue)) { - document.documentElement.setAttribute('data-color-mode', rememberedValue); - } - const button = document.querySelector('#darkMode'); - button.onclick = () => { - const theme = document.documentElement.dataset.colorMode; - const mode = theme === 'light' ? 'dark' : 'light'; - document.documentElement.setAttribute('data-color-mode', mode); - localStorage.setItem(LOCAL_NANE, mode); - } -`; - const ICONS_PATH = path.resolve(process.cwd(), 'scripts/assets'); export function darkMode() { @@ -33,15 +18,5 @@ export function darkMode() { }, children: [...sunNode, ...moonNode], }, - { - type: 'element', - tagName: 'script', - children: [ - { - type: 'text', - value: scripts, - }, - ], - }, ]; } diff --git a/scripts/watch.mjs b/scripts/watch.mjs index 1456672..c4c7579 100644 --- a/scripts/watch.mjs +++ b/scripts/watch.mjs @@ -1,20 +1,32 @@ import path from 'path'; import chokidar from 'chokidar'; import { getStat } from 'recursive-readdir-files'; -import { run, DOCS, createHTML } from './index.mjs'; +import { run, DOCS, createHTML, copyCSSFile, copyJSFile } from './index.mjs'; (async () => { await run(); const homeMdPath = path.relative(process.cwd(), 'README.md'); - const watcher = chokidar.watch([DOCS, homeMdPath], { + const cssDirPath = path.relative(process.cwd(), 'scripts/style'); + const jsDirPath = path.relative(process.cwd(), 'scripts/js'); + const watcher = chokidar.watch([DOCS, homeMdPath, cssDirPath, jsDirPath], { ignored: /(^|[\/\\])\../, // ignore dotfiles persistent: true, }); watcher - .on('change', async (path) => { - const stats = await getStat(path); - createHTML([stats]); + .on('change', async (filepath) => { + if (filepath.endsWith('.md')) { + const stats = await getStat(filepath); + createHTML([stats]); + } + if (filepath.endsWith('.css')) { + copyCSSFile(filepath); + console.log(`♻️ \x1b[32;1m ${path.relative(cssDirPath, filepath)} \x1b[0m`); + } + if (filepath.endsWith('.js')) { + copyJSFile(filepath); + console.log(`♻️ \x1b[32;1m ${path.relative(jsDirPath, filepath)} \x1b[0m`); + } }) .on('error', (error) => console.log(`Watcher error: ${error}`)); })();