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