website: add search feature. #32
This commit is contained in:
parent
9290c65fdc
commit
3bd4114c63
3
scripts/assets/search.svg
Normal file
3
scripts/assets/search.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 18 18">
|
||||
<path fill="currentColor" d="M17.71,16.29 L14.31,12.9 C15.4069846,11.5024547 16.0022094,9.77665502 16,8 C16,3.581722 12.418278,0 8,0 C3.581722,0 0,3.581722 0,8 C0,12.418278 3.581722,16 8,16 C9.77665502,16.0022094 11.5024547,15.4069846 12.9,14.31 L16.29,17.71 C16.4777666,17.8993127 16.7333625,18.0057983 17,18.0057983 C17.2666375,18.0057983 17.5222334,17.8993127 17.71,17.71 C17.8993127,17.5222334 18.0057983,17.2666375 18.0057983,17 C18.0057983,16.7333625 17.8993127,16.4777666 17.71,16.29 Z M2,8 C2,4.6862915 4.6862915,2 8,2 C11.3137085,2 14,4.6862915 14,8 C14,11.3137085 11.3137085,14 8,14 C4.6862915,14 2,11.3137085 2,8 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 723 B |
@ -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));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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)$/,
|
||||
|
9
scripts/js/fuse.min.js
vendored
Normal file
9
scripts/js/fuse.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
153
scripts/js/main.js
Normal file
153
scripts/js/main.js
Normal file
@ -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 `<mark>${txt}</mark>`
|
||||
})
|
||||
const href = isHome ? item.item.path : item.item.path.replace('docs/', '');
|
||||
if (idx === 0) {
|
||||
menuHTML += `<a href="${href}" class="active">${label}</a>`;
|
||||
} else {
|
||||
menuHTML += `<a href="${href}">${label}</a>`;
|
||||
}
|
||||
});
|
||||
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 `<mark>${txt}</mark>`
|
||||
});
|
||||
let sectionHTML = `<h3>${title}</h3><ol>`;
|
||||
if (data && data.item && data.item.sections) {
|
||||
data.item.sections.forEach((item, idx) => {
|
||||
const label = item.t.replace(new RegExp(inputValue, 'ig'), (txt) => {
|
||||
return `<mark>${txt}</mark>`
|
||||
})
|
||||
const href = isHome ? data.item.path : data.item.path.replace('docs/', '');
|
||||
if (item.l < 3) {
|
||||
sectionHTML += `<li><a href="${href + item.a}">${label}</a><div>`
|
||||
} else {
|
||||
sectionHTML += `<a href="${href + item.a}">${label}</a>`
|
||||
}
|
||||
if (data.item.sections.length === idx + 1) {
|
||||
sectionHTML += `</div></li>`
|
||||
}
|
||||
})
|
||||
}
|
||||
searchContent.innerHTML = sectionHTML;
|
||||
}
|
@ -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);
|
||||
|
@ -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,
|
||||
|
107
scripts/nodes/search.mjs
Normal file
107
scripts/nodes/search.mjs
Normal file
@ -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: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
@ -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 {
|
||||
|
@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -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}`));
|
||||
})();
|
||||
|
Loading…
x
Reference in New Issue
Block a user