index.ts (3322B)
1 import { h, Fragment, onMounted } from 'vue' 2 import DefaultTheme from 'vitepress/theme' 3 import type { Theme } from 'vitepress' 4 import Banner from './components/Banner.vue' 5 import Footer from './components/Footer.vue' 6 import SponsorButton from './components/SponsorButton.vue' 7 import ExternalLinkWarning from './components/ExternalLinkWarning.vue' 8 import CopyLinkButton from './components/CopyLinkButton.vue' 9 import MarkdownMenu from './components/MarkdownMenu.vue' 10 import Breadcrumbs from './components/Breadcrumbs.vue' 11 import Labels from './components/Labels.vue' 12 import SidebarTags from './components/SidebarTags.vue' 13 import './style.css' 14 15 export default { 16 extends: DefaultTheme, 17 18 Layout() { 19 return h(DefaultTheme.Layout, null, { 20 'layout-bottom': () => h(Fragment, [ 21 h(Banner), 22 h(ExternalLinkWarning) 23 ]), 24 'doc-before': () => h('div', { class: 'bsc-doc-toolbar' }, [ 25 h(Breadcrumbs), 26 h(CopyLinkButton) 27 ]), 28 'doc-footer-before': () => h(Fragment, [ 29 h(MarkdownMenu), 30 h(Labels) 31 ]), 32 'doc-after': () => h(Footer), 33 'sidebar-nav-after': () => h(SidebarTags), 34 'nav-bar-content-after': () => h(SponsorButton) 35 }) 36 }, 37 38 setup() { 39 onMounted(() => { 40 setTimeout(() => { 41 const searchBtn = document.querySelector('.VPNavBarSearchButton'); 42 const askAiBtn = document.querySelector('.VPNavBarAskAiButton'); 43 const triggerBtns = [searchBtn, askAiBtn].filter(Boolean) as Element[]; 44 if (!triggerBtns.length) return; 45 46 let prefetched = false; 47 const prefetchDocSearch = () => { 48 if (prefetched) return; 49 prefetched = true; 50 Promise.all([ 51 import('@docsearch/js'), 52 import('@docsearch/sidepanel-js') 53 ]).catch(() => { 54 prefetched = false; 55 }); 56 }; 57 58 for (const btn of triggerBtns) { 59 btn.addEventListener('mouseenter', prefetchDocSearch, { once: true }); 60 btn.addEventListener('focus', prefetchDocSearch, { once: true }); 61 btn.addEventListener('touchstart', prefetchDocSearch, { once: true, passive: true }); 62 btn.addEventListener('click', prefetchDocSearch, { once: true }); 63 } 64 65 window.addEventListener('keydown', (e) => { 66 const key = e.key.toLowerCase(); 67 const isShortcut = 68 ((e.ctrlKey || e.metaKey) && (key === 'k' || key === 'i')) || 69 key === '/'; 70 if (isShortcut) prefetchDocSearch(); 71 }); 72 73 for (const btn of triggerBtns) { 74 btn.addEventListener('click', () => { 75 if ( 76 !document.querySelector('.DocSearch-Modal') && 77 !document.querySelector('#docsearch-sidepanel') 78 ) { 79 btn.classList.add('is-loading'); 80 } 81 }); 82 } 83 84 const observer = new MutationObserver(() => { 85 const isOpen = 86 document.querySelector('.DocSearch-Modal') || 87 document.querySelector('#docsearch-sidepanel'); 88 if (isOpen) { 89 for (const btn of triggerBtns) btn.classList.remove('is-loading'); 90 } 91 }); 92 observer.observe(document.body, { childList: true, subtree: true }); 93 }, 500); 94 }) 95 } 96 } satisfies Theme