Initial rearrangment of vuetom site source

This commit is contained in:
2024-10-09 08:23:08 +00:00
parent dbd456a517
commit e9d08bd263
233 changed files with 22841 additions and 1 deletions

View File

@@ -0,0 +1,10 @@
<template>
<main
class="page">
<div class="container">
<slot name="top" />
<Content class="content" />
<slot name="bottom" />
</div>
</main>
</template>

View File

@@ -0,0 +1,126 @@
<script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import docsearch from '@docsearch/js'
import { onMounted } from 'vue'
import { useRouter, useRoute, useData } from 'vitepress'
const router = useRouter()
const route = useRoute()
const { theme } = useData()
onMounted(() => {
initialize(theme.value.algolia)
setTimeout(poll, 16)
})
function poll() {
// programmatically open the search box after initialize
const e = new Event('keydown') as any
e.key = 'k'
e.metaKey = true
window.dispatchEvent(e)
setTimeout(() => {
if (!document.querySelector('.DocSearch-Modal')) {
poll()
}
}, 16)
}
function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
// note: multi-lang search support is removed since the theme
// doesn't support multiple locales as of now.
const options = Object.assign({}, userOptions, {
container: '#docsearch',
navigator: {
navigate({ itemUrl }: { itemUrl: string }) {
const { pathname: hitPathname } = new URL(
window.location.origin + itemUrl
)
// router doesn't handle same-page navigation so we use the native
// browser location API for anchor navigation
if (route.path === hitPathname) {
window.location.assign(window.location.origin + itemUrl)
} else {
router.go(itemUrl)
}
}
},
transformItems(items: any[]) {
return items.map((item) => {
return Object.assign({}, item, {
url: getRelativePath(item.url)
})
})
},
hitComponent({ hit, children }: { hit: any; children: any }) {
const relativeHit = hit.url.startsWith('http')
? getRelativePath(hit.url as string)
: hit.url
return {
__v: null,
type: 'a',
ref: undefined,
constructor: undefined,
key: undefined,
props: {
href: hit.url,
onClick(event: MouseEvent) {
if (isSpecialClick(event)) {
return
}
// we rely on the native link scrolling when user is already on
// the right anchor because Router doesn't support duplicated
// history entries.
if (route.path === relativeHit) {
return
}
// if the hits goes to another page, we prevent the native link
// behavior to leverage the Router loading feature.
if (route.path !== relativeHit) {
event.preventDefault()
}
router.go(relativeHit)
},
children
}
}
}
})
docsearch(options)
}
function isSpecialClick(event: MouseEvent) {
return (
event.button === 1 ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
)
}
function getRelativePath(absoluteUrl: string) {
const { pathname, hash } = new URL(absoluteUrl)
return pathname + hash
}
</script>
<template>
<div id="docsearch" />
</template>

View File

@@ -0,0 +1,81 @@
<!-- @format -->
<script setup lang="ts">
import { useRoute, useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar.js'
import NotFound from '../layouts/NotFound.vue'
import VTHome from './VTHome.vue'
import VTDoc from './article/VTDoc.vue'
import VTDocList from './article/VTDocList.vue'
import VTSidebar from './sidebar/VTSidebar.vue'
const route = useRoute()
const { frontmatter } = useData()
const { hasSidebar } = useSidebar()
</script>
<template>
<div
class="VPContent"
id="VPContent"
:class="{
'has-sidebar': hasSidebar,
'is-home': frontmatter.layout === 'home',
}"
>
<NotFound v-if="route.component === NotFound" />
<VTHome
v-else-if="frontmatter.layout === 'home' || frontmatter.layout === 'doc'"
>
<template #sidebar><VTSidebar /></template>
<template #doclist>
<!-- 文章列表 -->
<VTDocList v-if="frontmatter.layout === 'home'" />
</template>
<template #docone>
<!-- 单个文章 -->
<VTDoc v-if="frontmatter.layout === 'doc'">
<Content />
</VTDoc>
</template>
</VTHome>
<div v-else>
<Content></Content>
</div>
</div>
</template>
<style scoped>
.VPContent {
flex-grow: 1;
flex-shrink: 0;
margin: 0 auto;
width: 100%;
}
.VPContent.is-home {
width: 100%;
max-width: 100%;
}
@media (min-width: 960px) {
.VPContent {
padding-top: var(--vp-nav-height);
}
.VPContent.has-sidebar {
margin: 0;
padding-left: var(--vp-sidebar-width);
}
}
@media (min-width: 1440px) {
.VPContent.has-sidebar {
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2);
padding-left: calc(
(100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)
);
}
}
</style>

View File

@@ -0,0 +1,156 @@
<script lang="ts" setup>
import { ref } from 'vue'
import { useFlyout } from '../composables/flyout.js'
import VPIconChevronDown from './icons/VPIconChevronDown.vue'
import VPIconMoreHorizontal from './icons/VPIconMoreHorizontal.vue'
import VPMenu from './VPMenu.vue'
defineProps<{
icon?: any
button?: string
label?: string
items?: any[]
}>()
const open = ref(false)
const el = ref<HTMLElement>()
useFlyout({ el, onBlur })
function onBlur() {
open.value = false
}
</script>
<template>
<div
class="VPFlyout"
ref="el"
@mouseenter="open = true"
@mouseleave="open = false"
>
<button
type="button"
class="button"
aria-haspopup="true"
:aria-expanded="open"
:aria-label="label"
@click="open = !open"
>
<span v-if="button || icon" class="text">
<component v-if="icon" :is="icon" class="option-icon" />
{{ button }}
<VPIconChevronDown class="text-icon" />
</span>
<VPIconMoreHorizontal v-else class="icon" />
</button>
<div class="menu">
<VPMenu :items="items">
<slot />
</VPMenu>
</div>
</div>
</template>
<style scoped>
.VPFlyout {
position: relative;
}
.VPFlyout:hover {
color: var(--vp-c-bland);
transition: color 0.25s;
}
.VPFlyout:hover .text {
color: var(--vp-c-text-2);
}
.VPFlyout:hover .icon {
fill: var(--vp-c-text-2);
}
.VPFlyout.active .text {
color: var(--vp-c-brand);
}
.VPFlyout.active:hover .text {
color: var(--vp-c-brand-dark);
}
.VPFlyout:hover .menu,
.button[aria-expanded="true"] + .menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.button {
display: flex;
align-items: center;
padding: 0 12px;
height: var(--vp-nav-height-mobile);
color: var(--vp-c-text-1);
transition: color 0.5s;
}
@media (min-width: 960px) {
.button {
height: var(--vp-nav-height-desktop);
}
}
.text {
display: flex;
align-items: center;
line-height: var(--vp-nav-height-mobile);
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-1);
transition: color 0.25s;
}
@media (min-width: 960px) {
.text {
line-height: var(--vp-nav-height-desktop);
}
}
.option-icon {
margin-right: 0px;
width: 16px;
height: 16px;
fill: currentColor;
}
.text-icon {
margin-left: 4px;
width: 14px;
height: 14px;
fill: currentColor;
}
.icon {
width: 20px;
height: 20px;
fill: currentColor;
transition: fill 0.25s;
}
.menu {
position: absolute;
top: calc(var(--vp-nav-height-mobile) / 2 + 20px);
right: 0;
opacity: 0;
visibility: hidden;
transition: opacity 0.25s, visibility 0.25s, transform 0.25s;
}
@media (min-width: 960px) {
.menu {
top: calc(var(--vp-nav-height-desktop) / 2 + 20px);
}
}
</style>

View File

@@ -0,0 +1,84 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useData } from 'vitepress'
import { useSidebar } from '../composables/sidebar.js'
import VTLeftButton from './VTLeftButton.vue'
const { theme } = useData()
const { hasSidebar } = useSidebar()
const brandColor = ref('')
const colorRef = ref(null)
const changeBrandColor = (color: string) => {
brandColor.value = color
document.documentElement.style.setProperty('--vp-c-brand', color)
}
</script>
<template>
<footer
v-if="theme.footer"
class="VTFooter relative left-0 right-0 bottom-0 container mx-auto w-full pb-8"
:class="{ 'has-sidebar': hasSidebar }"
>
<!-- <div class="grid grid-cols-4 gap-10"> -->
<div class="flex">
<div
id="VTFooterLeft"
class="rounded-vt hidden w-64 px-4 flex-none md:block">
<VTLeftButton>
<div ref="colorRef">
<span @click="changeBrandColor('red')">red</span>
<span @click="changeBrandColor('green')" >green</span>
</div>
</VTLeftButton>
</div>
<div
id="VTFooterMiddle"
class="rounded-vt w-full mt-8 px-4 md:w-1/2 flex-grow">
<div class="rounded-vt bg-cbg w-4/5 mx-auto text-center py-8">
<p class="message">{{ theme.footer.message }}</p>
<p class="copyright">{{ theme.footer.copyright }}</p>
</div>
</div>
</div>
</footer>
</template>
<style scoped>
/* .VPFooter {
position: absolute;
z-index: var(--vp-z-index-footer);
border-top: 1px solid var(--vp-c-divider-light);
padding: 32px 24px;
background-color: var(--vp-c-bg);
width: 80%;
left: 10%;
border-radius: 10px;
bottom: 20px;
} */
.VPFooter.has-sidebar {
display: none;
}
@media (min-width: 768px) {
.VPFooter {
/* padding: 32px; */
}
}
.message,
.copyright {
line-height: 24px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.message {
order: 2;
}
.copyright {
order: 1;
}
</style>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import { withBase } from 'vitepress'
defineProps<{
image: DefaultTheme.ThemeableImage
}>()
</script>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>
<template>
<template v-if="image">
<img
v-if="typeof image === 'string' || 'src' in image"
class="VPImage"
v-bind="typeof image === 'string' ? $attrs : { ...image, ...$attrs }"
:src="withBase(typeof image === 'string' ? image : image.src)"
/>
<template v-else>
<VPImage class="dark" :image="image.dark" v-bind="$attrs" />
<VPImage class="light" :image="image.light" v-bind="$attrs" />
</template>
</template>
</template>
<style scoped>
html:not(.dark) .VPImage.dark {
display: none;
}
.dark .VPImage.light {
display: none;
}
</style>

View File

@@ -0,0 +1,38 @@
<script lang="ts" setup>
import { computed } from 'vue'
import { normalizeLink } from '../support/utils.js'
import VPIconExternalLink from './icons/VPIconExternalLink.vue'
const props = defineProps<{
href?: string
noIcon?: boolean
}>()
const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href))
</script>
<template>
<component
:is="href ? 'a' : 'span'"
class="VPLink"
:class="{ link: href }"
:href="href ? normalizeLink(href) : undefined"
:target="isExternal ? '_blank' : undefined"
:rel="isExternal ? 'noopener noreferrer' : undefined"
>
<slot />
<VPIconExternalLink v-if="isExternal && !noIcon" class="icon" />
</component>
</template>
<style scoped>
.icon {
display: inline-block;
margin-top: -1px;
margin-left: 4px;
width: 11px;
height: 11px;
fill: var(--vp-c-text-3);
transition: fill 0.25s;
}
</style>

View File

@@ -0,0 +1,74 @@
<script lang="ts" setup>
import VPMenuLink from './VPMenuLink.vue'
import VPMenuGroup from './VPMenuGroup.vue'
defineProps<{
items?: any[]
}>()
</script>
<template>
<div class="VPMenu">
<div v-if="items" class="items">
<template v-for="item in items" :key="item.text">
<VPMenuLink v-if="'link' in item" :item="item" />
<VPMenuGroup v-else :text="item.text" :items="item.items" />
</template>
</div>
<slot />
</div>
</template>
<style scoped>
.VPMenu {
border-radius: 12px;
padding: 12px;
min-width: 128px;
border: 1px solid var(--vp-c-divider-light);
background-color: var(--vp-c-bg);
box-shadow: var(--vp-shadow-3);
transition: background-color 0.5s;
}
.dark .VPMenu {
box-shadow: var(--vp-shadow-2);
}
.VPMenu :deep(.group) {
margin: 0 -12px;
padding: 0 12px 12px;
}
.VPMenu :deep(.group + .group) {
border-top: 1px solid var(--vp-c-divider-light);
padding: 11px 12px 12px;
}
.VPMenu :deep(.group:last-child) {
padding-bottom: 0;
}
.VPMenu :deep(.group + .item) {
border-top: 1px solid var(--vp-c-divider-light);
padding: 11px 16px 0;
}
.VPMenu :deep(.item) {
padding: 0 16px;
white-space: nowrap;
}
.VPMenu :deep(.label) {
flex-grow: 1;
line-height: 28px;
font-size: 12px;
font-weight: 500;
color: var(--vp-c-text-2);
transition: color .5s;
}
.VPMenu :deep(.action) {
padding-left: 24px;
}
</style>

View File

@@ -0,0 +1,46 @@
<script lang="ts" setup>
import VPMenuLink from './VPMenuLink.vue'
defineProps<{
text?: string
items: any[]
}>()
</script>
<template>
<div class="VPMenuGroup">
<p v-if="text" class="title">{{ text }}</p>
<template v-for="item in items">
<VPMenuLink v-if="'link' in item" :item="item" />
</template>
</div>
</template>
<style scoped>
.VPMenuGroup {
margin: 12px -12px 0;
border-top: 1px solid var(--vp-c-divider-light);
padding: 12px 12px 0;
}
.VPMenuGroup:first-child {
margin-top: 0;
border-top: 0;
padding-top: 0;
}
.VPMenuGroup + .VPMenuGroup {
margin-top: 12px;
border-top: 1px solid var(--vp-c-divider-light);
}
.title {
padding: 0 12px;
line-height: 32px;
font-size: 14px;
font-weight: 600;
color: var(--vp-c-text-2);
transition: color 0.25s;
}
</style>

View File

@@ -0,0 +1,55 @@
<script lang="ts" setup>
import { useData } from 'vitepress'
import { isActive } from '../support/utils.js'
import VPLink from './VPLink.vue'
defineProps<{
item: any
}>()
const { page } = useData()
</script>
<template>
<div class="VPMenuLink">
<VPLink
:class="{ active: isActive(page.relativePath, item.activeMatch || item.link) }"
:href="item.link"
>
{{ item.text }}
</VPLink>
</div>
</template>
<style scoped>
.VPMenuGroup + .VPMenuLink {
margin: 12px -12px 0;
border-top: 1px solid var(--vp-c-divider-light);
padding: 12px 12px 0;
}
.link {
display: block;
border-radius: 6px;
padding: 0 12px;
line-height: 32px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-1);
white-space: nowrap;
transition: background-color 0.25s, color 0.25s;
}
.link:hover {
color: var(--vp-c-brand);
background-color: var(--vp-c-bg-mute);
}
.dark .link:hover {
background-color: var(--vp-c-bg-soft);
}
.link.active {
color: var(--vp-c-brand);
}
</style>

View File

@@ -0,0 +1,63 @@
<script lang="ts" setup>
import type { DefaultTheme } from 'vitepress/theme'
import VPIconDiscord from './icons/VPIconDiscord.vue'
import VPIconFacebook from './icons/VPIconFacebook.vue'
import VPIconGitHub from './icons/VPIconGitHub.vue'
import VPIconLinkedIn from './icons/VPIconLinkedIn.vue'
import VPIconInstagram from './icons/VPIconInstagram.vue'
import VPIconSlack from './icons/VPIconSlack.vue'
import VPIconTwitter from './icons/VPIconTwitter.vue'
import VPIconYouTube from './icons/VPIconYouTube.vue'
defineProps<{
icon: DefaultTheme.SocialLinkIcon
link: string
}>()
const icons = {
discord: VPIconDiscord,
facebook: VPIconFacebook,
github: VPIconGitHub,
instagram: VPIconInstagram,
linkedin: VPIconLinkedIn,
slack: VPIconSlack,
twitter: VPIconTwitter,
youtube: VPIconYouTube
}
</script>
<template>
<a
class="VPSocialLink"
:href="link"
:title="icon"
target="_blank"
rel="noopener noreferrer"
>
<component :is="icons[icon]" class="icon" />
<span class="visually-hidden">{{ icon }}</span>
</a>
</template>
<style scoped>
.VPSocialLink {
display: flex;
justify-content: center;
align-items: center;
width: 36px;
height: 36px;
color: var(--vp-c-text-2);
transition: color .5s;
}
.VPSocialLink:hover {
color: var(--vp-c-text-1);
transition: color .25s;
}
.icon {
width: 20px;
height: 20px;
fill: currentColor;
}
</style>

View File

@@ -0,0 +1,27 @@
<script lang="ts" setup>
import type { DefaultTheme } from 'vitepress/theme'
import VPSocialLink from './VPSocialLink.vue'
defineProps<{
links: DefaultTheme.SocialLink[]
}>()
</script>
<template>
<div class="VPSocialLinks">
<VPSocialLink
v-for="{ link, icon } in links"
:key="link"
:icon="icon"
:link="link"
/>
</div>
</template>
<style scoped>
.VPSocialLinks {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,87 @@
<script setup lang="ts">
import { onMounted, ref, computed, reactive } from 'vue'
import { useMediaQuery, useParallax } from '@vueuse/core'
import type { CSSProperties } from 'vue'
import { useHomeParallax } from '../composables/homebg.js'
const { parallaxEnable } = useHomeParallax()
onMounted(() => {})
const parallaxRef = ref(null)
const isMobile = useMediaQuery('(max-width: 700px)')
const parallax = reactive(useParallax(parallaxRef))
const cardWindowStyle: CSSProperties = {
overflow: 'hidden',
fontSize: '1rem',
position: 'absolute',
top: '0',
left: '0',
height: '100%',
width: '100%',
}
const layerBase: CSSProperties = {
position: 'absolute',
height: '100%',
width: '100%',
transition: '.3s ease-out all',
}
const layerBg = computed(() => ({
...layerBase,
transform: `translateX(${parallax.tilt * 10}px) translateY(${
parallax.roll * 10
}px) scale(1.1)`,
}))
const layerCloud = computed(() => ({
...layerBase,
transform: `translateX(${parallax.tilt * 200}px) translateY(${
parallax.roll * 30 - 100
}px) scale(1)`,
}))
const layerHuman = computed(() => ({
...layerBase,
transform: `translateX(${parallax.tilt * -50}px) translateY(${
parallax.roll * 30
}px) scale(1.2)`,
}))
const layerGrass = computed(() => ({
...layerBase,
transform: `translateX(${parallax.tilt * -200}px) translateY(${
parallax.roll * 100 - 120
}px) scale(1.5)`,
}))
const cardStyle = computed(() => ({
position: 'absolute',
zIndex: 1,
background: '#00000000',
height: '100%',
width: '100%',
borderRadius: '5px',
overflow: 'hidden',
transition: '.3s ease-out all',
boxShadow: '0 0 20px 0 rgba(255, 255, 255, 0.25)',
// transform: `rotateX(${parallax.roll * 20}deg) rotateY(${
// parallax.tilt * 20
// }deg)`
}))
</script>
<template>
<div class="VTBackgroud" ref="parallaxRef">
<div v-if="parallaxEnable" :style="cardStyle">
<div :style="cardWindowStyle">
<img :style="layerBg" src="/imgs/blog-bg-cloud.png" alt="" />
<img :style="layerCloud" src="/imgs/cloud2.png" alt="" />
<img :style="layerHuman" src="/imgs/blog-bg-human.png" alt="" />
<img :style="layerGrass" src="/imgs/blog-bg-grass.png" alt="" />
</div>
</div>
<slot />
</div>
</template>
<style scoped>
.VTBackground {
}
</style>

View File

@@ -0,0 +1,93 @@
<script lang="ts" setup>
import { ref } from 'vue'
import VPSwitchAppearance from './switch/VPSwitchAppearance.vue'
defineProps<{
icon?: any
button?: string
label?: string
items?: any[]
}>()
const open = ref(false)
const el = ref<HTMLElement>()
</script>
<template>
<div
class="VTFloat"
ref="el"
@mouseenter="open = true"
@mouseleave="open = false"
>
<div class="box hover:animate-wiggle">
<VPSwitchAppearance />
</div>
<div class="box hover:animate-wiggle">
Set
</div>
</div>
</template>
<style scoped>
.VTFloat {
display: flex;
flex-direction: column;
align-items: center;
position: fixed;
right: 20px;
bottom: 100px;
width: 60px;
height: max-content;
height: -moz-max-content;
z-index: 1000;
transition: all .3s ease;
}
.VTFloat:hover {
color: var(--vp-c-bland);
transition: color 0.25s;
}
.VTFloat:hover .text {
color: var(--vp-c-text-2);
}
.VTFloat:hover .icon {
fill: var(--vp-c-text-2);
}
.VTFloat.active .text {
color: var(--vp-c-brand);
}
.VTFloat.active:hover .text {
color: var(--vp-c-brand-dark);
}
.box {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
margin: 5px;
transition: opacity 0.25s, border-color 0.5s, transform 0.25s;
border-radius: 10px;
border: 1px solid var(--vp-c-divider-light);
background-color: var(--vp-c-bg);
}
.box:hover {
border-color: var(--vp-c-gray);
}
@media (min-width: 960px) {
.menu {
top: calc(var(--vp-nav-height-desktop) / 2 + 20px);
}
}
</style>

View File

@@ -0,0 +1,60 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import VPFooter from './VPFooter.vue'
onMounted(() => {
const homeDown = document.getElementById('home-down')
const top = homeDown.clientHeight - 110
window.scroll({
top,
left: 0,
behavior: 'smooth'
})
})
</script>
<template>
<div class="VTHome">
<div id="home-down" class="min-h-eight relative">
<div :class="[
'absolute bottom-5 left-0 right-0 animate-bounce ',
'mx-auto w-10 text-3xl text-white'
]">
<i class="fa fa-chevron-down"></i>
</div>
</div>
<div id="VTContainer" class="container mx-auto w-11/12 pb-8">
<div class="flex">
<!-- <div class="grid grid-cols-4 gap-10"> -->
<div id="VTLeft" :class="[
'rounded-vt hidden w-64 px-4 flex-none',
'md:block'
]">
<slot name="sidebar"></slot>
</div>
<div id="VTContent" class="rounded-vt w-full px-4 md:w-1/2 flex-grow">
<slot name="doclist"></slot>
<slot name="docone"></slot>
</div>
</div>
<VPFooter />
</div>
</div>
</template>
<style scoped>
.VTHome {
}
.VTHome :deep(.VTHomeSponsors) {
margin-top: 112px;
margin-bottom: -128px;
}
@media (min-width: 768px) {
.VTHome {
}
}
.home-vtp-btn-up {
}
</style>

View File

@@ -0,0 +1,383 @@
<template>
<div class="vt-btn-up-wrapper">
<input type="checkbox" />
<div class="btn"></div>
<div class="tooltip ">
<slot>
</slot>
</div>
<svg>
<use xlink:href="#shape-01" class="shape shape-01" />
<use xlink:href="#shape-02" class="shape shape-02" />
<use xlink:href="#shape-03" class="shape shape-03" />
<use xlink:href="#shape-04" class="shape shape-04" />
<use xlink:href="#shape-05" class="shape shape-05" />
<use xlink:href="#shape-06" class="shape shape-06" />
<use xlink:href="#shape-07" class="shape shape-07" />
<use xlink:href="#shape-08" class="shape shape-08" />
<use xlink:href="#shape-09" class="shape shape-09" />
</svg>
</div>
</template>
<style scoped>
.vt-btn-up-wrapper {
--background: #62abff;
--icon-color: #414856;
--shape-color-01: #b8cbee;
--shape-color-02: #7691e8;
--shape-color-03: #fdd053;
--width: 40px;
--height: 40px;
--border-radius: var(--height);
width: var(--width);
height: var(--height);
position: relative;
border-radius: var(--border-radius);
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
-webkit-box-align: center;
align-items: center;
}
.vt-btn-up-wrapper .btn {
background: var(--background);
width: var(--width);
height: var(--height);
position: relative;
z-index: 3;
border-radius: var(--border-radius);
box-shadow: 0 10px 30px rgba(65, 72, 86, 0.05);
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
-webkit-box-align: center;
align-items: center;
-webkit-animation: plus-animation-reverse 0.5s ease-out forwards;
animation: plus-animation-reverse 0.5s ease-out forwards;
}
.vt-btn-up-wrapper .btn::before,
.vt-btn-up-wrapper .btn::after {
content: '';
display: block;
position: absolute;
border-radius: 4px;
background: #fff;
}
.vt-btn-up-wrapper .btn::before {
width: 4px;
height: 14px;
}
.vt-btn-up-wrapper .btn::after {
width: 14px;
height: 4px;
}
.vt-btn-up-wrapper .tooltip {
/* width: 90px;
height: 75px; */
border-radius: 50px;
position: absolute;
background: #fff;
z-index: 2;
padding: 0 15px;
box-shadow: 0 10px 30px rgba(65, 72, 86, 0.1);
opacity: 0;
top: 0;
display: -webkit-box;
display: flex;
justify-content: space-around;
-webkit-box-align: center;
align-items: center;
-webkit-transition: opacity 0.15s ease-in, top 0.15s ease-in,
width 0.15s ease-in;
transition: opacity 0.15s ease-in, top 0.15s ease-in, width 0.15s ease-in;
}
.vt-btn-up-wrapper .tooltip:hover {
box-shadow: 0 10px 30px rgba(65, 72, 86, 0.2);
}
.vt-btn-up-wrapper .tooltip > svg {
width: 100%;
height: 26px;
display: -webkit-box;
display: flex;
justify-content: space-around;
-webkit-box-align: center;
align-items: center;
cursor: pointer;
}
.vt-btn-up-wrapper .tooltip > svg .icon {
fill: none;
stroke: var(--icon-color);
stroke-width: 2px;
stroke-linecap: round;
stroke-linejoin: round;
opacity: 0.4;
-webkit-transition: opacity 0.3s ease;
transition: opacity 0.3s ease;
}
.vt-btn-up-wrapper .tooltip > svg:hover .icon {
opacity: 1;
}
.vt-btn-up-wrapper .tooltip::after {
content: '';
width: 20px;
height: 20px;
background: #fff;
border-radius: 3px;
position: absolute;
left: 2.6rem;
bottom: -0.5rem;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
z-index: 0;
}
.vt-btn-up-wrapper > svg {
width: 30px;
height: 30px;
position: absolute;
z-index: 1;
-webkit-transform: scale(0);
transform: scale(0);
}
.vt-btn-up-wrapper > svg .shape {
fill: none;
stroke: none;
stroke-width: 3px;
stroke-linecap: round;
stroke-linejoin: round;
-webkit-transform-origin: 50% 20%;
transform-origin: 50% 20%;
}
.vt-btn-up-wrapper input {
height: 100%;
width: 100%;
border-radius: var(--border-radius);
cursor: pointer;
position: absolute;
z-index: 5;
opacity: 0;
}
.vt-btn-up-wrapper input:checked ~ svg {
-webkit-animation: pang-animation 1.2s ease-out forwards;
animation: pang-animation 1.2s ease-out forwards;
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(1) {
-webkit-transform: translate(-17px, 30%) rotate(40deg);
transform: translate(-17px, 30%) rotate(40deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(2) {
-webkit-transform: translate(15px, 30%) rotate(80deg);
transform: translate(15px, 30%) rotate(80deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(3) {
-webkit-transform: translate(11px, 30%) rotate(120deg);
transform: translate(11px, 30%) rotate(120deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(4) {
-webkit-transform: translate(20px, 30%) rotate(160deg);
transform: translate(20px, 30%) rotate(160deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(5) {
-webkit-transform: translate(-20px, 30%) rotate(200deg);
transform: translate(-20px, 30%) rotate(200deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(6) {
-webkit-transform: translate(10px, 30%) rotate(240deg);
transform: translate(10px, 30%) rotate(240deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(7) {
-webkit-transform: translate(3px, 30%) rotate(280deg);
transform: translate(3px, 30%) rotate(280deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(8) {
-webkit-transform: translate(0px, 30%) rotate(320deg);
transform: translate(0px, 30%) rotate(320deg);
}
.vt-btn-up-wrapper input:checked ~ svg .shape:nth-of-type(9) {
-webkit-transform: translate(25px, 30%) rotate(360deg);
transform: translate(25px, 30%) rotate(360deg);
}
.vt-btn-up-wrapper input:checked ~ .btn {
-webkit-animation: plus-animation 0.5s ease-out forwards;
animation: plus-animation 0.5s ease-out forwards;
}
.vt-btn-up-wrapper input:checked ~ .tooltip {
width: 20rem;
height: 20rem;
-webkit-animation: stretch-animation 1s ease-out forwards 0.15s;
animation: stretch-animation 1s ease-out forwards 0.15s;
top: -22rem;
left: -2rem;
opacity: 1;
}
@-webkit-keyframes pang-animation {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0;
}
40% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
100% {
-webkit-transform: scale(1.1);
transform: scale(1.1);
opacity: 0;
}
}
@keyframes pang-animation {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0;
}
40% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
100% {
-webkit-transform: scale(1.1);
transform: scale(1.1);
opacity: 0;
}
}
@-webkit-keyframes plus-animation {
0% {
-webkit-transform: rotate(0) scale(1);
transform: rotate(0) scale(1);
}
20% {
-webkit-transform: rotate(60deg) scale(0.93);
transform: rotate(60deg) scale(0.93);
}
55% {
-webkit-transform: rotate(35deg) scale(0.97);
transform: rotate(35deg) scale(0.97);
}
80% {
-webkit-transform: rotate(48deg) scale(0.94);
transform: rotate(48deg) scale(0.94);
}
100% {
-webkit-transform: rotate(45deg) scale(0.95);
transform: rotate(45deg) scale(0.95);
}
}
@keyframes plus-animation {
0% {
-webkit-transform: rotate(0) scale(1);
transform: rotate(0) scale(1);
}
20% {
-webkit-transform: rotate(60deg) scale(0.93);
transform: rotate(60deg) scale(0.93);
}
55% {
-webkit-transform: rotate(35deg) scale(0.97);
transform: rotate(35deg) scale(0.97);
}
80% {
-webkit-transform: rotate(48deg) scale(0.94);
transform: rotate(48deg) scale(0.94);
}
100% {
-webkit-transform: rotate(45deg) scale(0.95);
transform: rotate(45deg) scale(0.95);
}
}
@-webkit-keyframes plus-animation-reverse {
0% {
-webkit-transform: rotate(45deg) scale(0.95);
transform: rotate(45deg) scale(0.95);
}
20% {
-webkit-transform: rotate(-15deg);
transform: rotate(-15deg);
}
55% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg);
}
80% {
-webkit-transform: rotate(-3deg);
transform: rotate(-3deg);
}
100% {
-webkit-transform: rotate(0) scale(1);
transform: rotate(0) scale(1);
}
}
@keyframes plus-animation-reverse {
0% {
-webkit-transform: rotate(45deg) scale(0.95);
transform: rotate(45deg) scale(0.95);
}
20% {
-webkit-transform: rotate(-15deg);
transform: rotate(-15deg);
}
55% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg);
}
80% {
-webkit-transform: rotate(-3deg);
transform: rotate(-3deg);
}
100% {
-webkit-transform: rotate(0) scale(1);
transform: rotate(0) scale(1);
}
}
@-webkit-keyframes stretch-animation {
0% {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
10% {
-webkit-transform: scale(1.1, 0.9);
transform: scale(1.1, 0.9);
}
30% {
-webkit-transform: scale(0.9, 1.1);
transform: scale(0.9, 1.1);
}
50% {
-webkit-transform: scale(1.05, 0.95);
transform: scale(1.05, 0.95);
}
100% {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
}
@keyframes stretch-animation {
0% {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
10% {
-webkit-transform: scale(1.1, 0.9);
transform: scale(1.1, 0.9);
}
30% {
-webkit-transform: scale(0.9, 1.1);
transform: scale(0.9, 1.1);
}
50% {
-webkit-transform: scale(1.05, 0.95);
transform: scale(1.05, 0.95);
}
100% {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
}
</style>

View File

@@ -0,0 +1,11 @@
<template>
<div class="bg-cbg rounded-vt shadow-vt p-10">
<div class="vp-doc">
<slot></slot>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,56 @@
<script setup lang="ts">
import { normalizeLink, fmt } from '../../support/utils.js'
const props = defineProps<{
data: any
}>()
const f = props.data.data.frontmatter
const tags = f.tags.split(',')
const timeStr = fmt(f.time)
const togo = (url: string) => {
if (!url) url = ''
window.location.href = normalizeLink(url)
}
</script>
<template>
<div class="VTDocCard">
<div class="rounded-vt h-80 w-full mb-6 bg-gray-700" @click="togo(data.path)">
<div class="pic h-1/2 text-white relative text-center">
<div class="title absolute w-4/5 bottom-2 left-0 right-0 mx-auto">
<h1 class="font-black text-xl antialiased tracking-wide">{{f.title}}</h1>
<p class="m-1">{{timeStr}}</p>
</div>
</div>
<div class="text h-1/2 bg-cbg rounded-vt px-10 py-6">
<div class="desc h-3/4">
<!-- {{data}} -->
</div>
<div class="tag h-1/4">
<span class="inline-block w-24 font-bold
hover:text-brand transition-colors duration-300">
<i class="fa fa-star-half-o" aria-hidden="true"></i>
&nbsp;
{{f.categories}}
</span>
<span>
<i class="fa fa-tags" aria-hidden="true"></i>
<span
class="px-1.5 m-1 inline-block text-center
bg-cbgs border-2 border-cbgs rounded-vt
hover:border-brand transition-colors duration-300
"
v-for="t in tags" :key="t">{{t}}</span>
</span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import { getRoutes } from '../../../support/pages.js'
import VTDocCard from './VTDocCard.vue'
const { routes } = getRoutes()
</script>
<template>
<div class="VTDoc">
<div v-for="(r,i) in routes" :key="r.__hmrId">
<VTDocCard :data="r" />
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,8 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M21,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,11,21,11z" />
<path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z" />
<path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z" />
<path d="M21,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,19,21,19z" />
</svg>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z" />
<path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z" />
<path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z" />
<path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z" />
</svg>
</template>

View File

@@ -0,0 +1,8 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M21,11H7c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S21.6,11,21,11z" />
<path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z" />
<path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z" />
<path d="M21,19H7c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S21.6,19,21,19z" />
</svg>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z"
/>
</svg>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"
/>
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M15,19c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4l6-6c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4L10.4,12l5.3,5.3c0.4,0.4,0.4,1,0,1.4C15.5,18.9,15.3,19,15,19z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M18,16c-0.3,0-0.5-0.1-0.7-0.3L12,10.4l-5.3,5.3c-0.4,0.4-1,0.4-1.4,0s-0.4-1,0-1.4l6-6c0.4-0.4,1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4C18.5,15.9,18.3,16,18,16z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M20.222 0c1.406 0 2.54 1.137 2.607 2.475V24l-2.677-2.273-1.47-1.338-1.604-1.398.67 2.205H3.71c-1.402 0-2.54-1.065-2.54-2.476V2.48C1.17 1.142 2.31.003 3.715.003h16.5L20.222 0zm-6.118 5.683h-.03l-.202.2c2.073.6 3.076 1.537 3.076 1.537-1.336-.668-2.54-1.002-3.744-1.137-.87-.135-1.74-.064-2.475 0h-.2c-.47 0-1.47.2-2.81.735-.467.203-.735.336-.735.336s1.002-1.002 3.21-1.537l-.135-.135s-1.672-.064-3.477 1.27c0 0-1.805 3.144-1.805 7.02 0 0 1 1.74 3.743 1.806 0 0 .4-.533.805-1.002-1.54-.468-2.14-1.404-2.14-1.404s.134.066.335.2h.06c.03 0 .044.015.06.03v.006c.016.016.03.03.06.03.33.136.66.27.93.4.466.202 1.065.403 1.8.536.93.135 1.996.2 3.21 0 .6-.135 1.2-.267 1.8-.535.39-.2.87-.4 1.397-.737 0 0-.6.936-2.205 1.404.33.466.795 1 .795 1 2.744-.06 3.81-1.8 3.87-1.726 0-3.87-1.815-7.02-1.815-7.02-1.635-1.214-3.165-1.26-3.435-1.26l.056-.02zm.168 4.413c.703 0 1.27.6 1.27 1.335 0 .74-.57 1.34-1.27 1.34-.7 0-1.27-.6-1.27-1.334.002-.74.573-1.338 1.27-1.338zm-4.543 0c.7 0 1.266.6 1.266 1.335 0 .74-.57 1.34-1.27 1.34-.7 0-1.27-.6-1.27-1.334 0-.74.57-1.338 1.27-1.338z" />
</svg>
</template>

View File

@@ -0,0 +1,6 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z" />
<path d="M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z" />
</svg>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
focusable="false"
height="24px"
viewBox="0 0 24 24"
width="24px"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12,22.2c-0.3,0-0.5-0.1-0.7-0.3l-8.8-8.8c-2.5-2.5-2.5-6.7,0-9.2c2.5-2.5,6.7-2.5,9.2,0L12,4.3l0.4-0.4c0,0,0,0,0,0C13.6,2.7,15.2,2,16.9,2c0,0,0,0,0,0c1.7,0,3.4,0.7,4.6,1.9l0,0c1.2,1.2,1.9,2.9,1.9,4.6c0,1.7-0.7,3.4-1.9,4.6l-8.8,8.8C12.5,22.1,12.3,22.2,12,22.2zM7,4C5.9,4,4.7,4.4,3.9,5.3c-1.8,1.8-1.8,4.6,0,6.4l8.1,8.1l8.1-8.1c0.9-0.9,1.3-2,1.3-3.2c0-1.2-0.5-2.3-1.3-3.2l0,0C19.3,4.5,18.2,4,17,4c0,0,0,0,0,0c-1.2,0-2.3,0.5-3.2,1.3c0,0,0,0,0,0l-1.1,1.1c-0.4,0.4-1,0.4-1.4,0l-1.1-1.1C9.4,4.4,8.2,4,7,4z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12 0C8.74 0 8.333.015 7.053.072 5.775.132 4.905.333 4.14.63c-.789.306-1.459.717-2.126 1.384S.935 3.35.63 4.14C.333 4.905.131 5.775.072 7.053.012 8.333 0 8.74 0 12s.015 3.667.072 4.947c.06 1.277.261 2.148.558 2.913.306.788.717 1.459 1.384 2.126.667.666 1.336 1.079 2.126 1.384.766.296 1.636.499 2.913.558C8.333 23.988 8.74 24 12 24s3.667-.015 4.947-.072c1.277-.06 2.148-.262 2.913-.558.788-.306 1.459-.718 2.126-1.384.666-.667 1.079-1.335 1.384-2.126.296-.765.499-1.636.558-2.913.06-1.28.072-1.687.072-4.947s-.015-3.667-.072-4.947c-.06-1.277-.262-2.149-.558-2.913-.306-.789-.718-1.459-1.384-2.126C21.319 1.347 20.651.935 19.86.63c-.765-.297-1.636-.499-2.913-.558C15.667.012 15.26 0 12 0zm0 2.16c3.203 0 3.585.016 4.85.071 1.17.055 1.805.249 2.227.415.562.217.96.477 1.382.896.419.42.679.819.896 1.381.164.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.015 3.585-.074 4.85c-.061 1.17-.256 1.805-.421 2.227-.224.562-.479.96-.899 1.382-.419.419-.824.679-1.38.896-.42.164-1.065.36-2.235.413-1.274.057-1.649.07-4.859.07-3.211 0-3.586-.015-4.859-.074-1.171-.061-1.816-.256-2.236-.421-.569-.224-.96-.479-1.379-.899-.421-.419-.69-.824-.9-1.38-.165-.42-.359-1.065-.42-2.235-.045-1.26-.061-1.649-.061-4.844 0-3.196.016-3.586.061-4.861.061-1.17.255-1.814.42-2.234.21-.57.479-.96.9-1.381.419-.419.81-.689 1.379-.898.42-.166 1.051-.361 2.221-.421 1.275-.045 1.65-.06 4.859-.06l.045.03zm0 3.678c-3.405 0-6.162 2.76-6.162 6.162 0 3.405 2.76 6.162 6.162 6.162 3.405 0 6.162-2.76 6.162-6.162 0-3.405-2.76-6.162-6.162-6.162zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm7.846-10.405c0 .795-.646 1.44-1.44 1.44-.795 0-1.44-.646-1.44-1.44 0-.794.646-1.439 1.44-1.439.793-.001 1.44.645 1.44 1.439z" />
</svg>
</template>

View File

@@ -0,0 +1,9 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"></path>
<path
d=" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z "
class="css-c4d79v"
></path>
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M22,13H2a1,1,0,0,1,0-2H22a1,1,0,0,1,0,2Z" />
</svg>
</template>

View File

@@ -0,0 +1,6 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
<path d="M19,2H5C3.3,2,2,3.3,2,5v14c0,1.7,1.3,3,3,3h14c1.7,0,3-1.3,3-3V5C22,3.3,20.7,2,19,2zM20,19c0,0.6-0.4,1-1,1H5c-0.6,0-1-0.4-1-1V5c0-0.6,0.4-1,1-1h14c0.6,0,1,0.4,1,1V19z" />
<path d="M16,11H8c-0.6,0-1,0.4-1,1s0.4,1,1,1h8c0.6,0,1-0.4,1-1S16.6,11,16,11z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z" />
</svg>
</template>

View File

@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="2" />
<circle cx="19" cy="12" r="2" />
<circle cx="5" cy="12" r="2" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z" />
</svg>
</template>

View File

@@ -0,0 +1,6 @@
<template>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M19,2H5C3.3,2,2,3.3,2,5v14c0,1.7,1.3,3,3,3h14c1.7,0,3-1.3,3-3V5C22,3.3,20.7,2,19,2z M20,19c0,0.6-0.4,1-1,1H5c-0.6,0-1-0.4-1-1V5c0-0.6,0.4-1,1-1h14c0.6,0,1,0.4,1,1V19z" />
<path d="M16,11h-3V8c0-0.6-0.4-1-1-1s-1,0.4-1,1v3H8c-0.6,0-1,0.4-1,1s0.4,1,1,1h3v3c0,0.6,0.4,1,1,1s1-0.4,1-1v-3h3c0.6,0,1-0.4,1-1S16.6,11,16,11z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" />
</svg>
</template>

View File

@@ -0,0 +1,13 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z" />
<path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z" />
<path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z" />
<path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z" />
<path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z" />
<path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z" />
<path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z" />
<path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z" />
<path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" />
</svg>
</template>

View File

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg>
</template>

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import { useData } from 'vitepress'
import { useSidebar } from '../../composables/sidebar.js'
import VTSidebarTop from './VTSidebarTop.vue'
import VTSidebarBottom from './VTSidebarBottom.vue'
const { site, theme } = useData()
const { hasSidebar } = useSidebar()
const { base } = site.value
let { avatar, author } = theme.value
let baseUrl = base
if (base === '/' || base.endsWith('/')) {
baseUrl = base.substring(0, base.length - 1)
}
avatar = `${baseUrl}${avatar}`
author += ' '
</script>
<template>
<div
class="VTSidebar"
:class="{ 'has-sidebar': hasSidebar }"
>
<VTSidebarTop />
<div class="h-6"></div>
<VTSidebarBottom
:avatar="avatar"
:author="author"
/>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,76 @@
<script setup lang="ts">
const d = defineProps<{
avatar?: string,
author?: string
}>()
</script>
<template>
<div class="VTSidebarBottom">
<div class="rounded-vt shadow-vt bg-cbg p-6">
<div class="avatar w-full pt-6">
<div
class="bg-cover bg-center rounded-full h-24 w-24 bg-green-100 mx-auto"
:style="{'background-image': `url(${d.avatar})`}"
>
</div>
</div>
<div class="author">
<p class="text-center py-6 text-xl font-bold">{{d.author}}</p>
</div>
<div class="tag">
<div class="grid grid-cols-3 w-4/5 mx-auto text-center">
<div>
<p class="font-bold">1</p>
<p class="text-sm hover:text-brand-d hover:font-bold">文章</p>
</div>
<div>
<p class="font-bold">0</p>
<p class="text-sm hover:text-brand-d hover:font-bold">分类</p>
</div>
<div>
<p class="font-bold">10</p>
<p class="text-sm hover:text-brand-d hover:font-bold">标签</p>
</div>
</div>
</div>
<div class="self-links">
<div class="py-6 flex flex-wrap text-left text-sm">
<div class="w-1/2 py-1.5 pl-2 mt-1 hover:bg-cbgs rounded-vt">
<i class="w-3 h-3 text-sm mr-1 fa fa-ambulance"></i>&nbsp;
<span>Gitee</span>
</div>
<div class="w-1/2 py-1.5 pl-2 mt-1 hover:bg-cbgs rounded-vt">
<i class="w-3 h-3 text-sm mr-1 fa fa-github-alt"></i>&nbsp;
<span>Github</span>
</div>
<div class="w-1/2 py-1.5 pl-2 mt-1 hover:bg-cbgs rounded-vt">
<i class="w-3 h-3 text-sm mr-1 fa fa-envelope-o"></i>&nbsp;
<span>Email</span>
</div>
</div>
</div>
<!-- <div class="friend-links">
<div class="py-6 border-t-2 border-gray-100 border-opacity-50 text-center">
<div>
<a
class="border-b-2 border-gray-200 inline-block m-1 px-1 hover:border-gray-500 transition duration-500 border-opacity-50"
href="https://github.com/lauset"
>LauSET</a
>
</div>
<div>
<a
class="border-b-2 border-gray-200 inline-block m-1 px-1 hover:border-gray-500 transition duration-500 border-opacity-50"
href="https://github.com/lauset"
>Vuetom</a
>
</div>
</div>
</div> -->
</div>
</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import { normalizeLink } from '../../support/utils.js'
const props = defineProps<{
text: string,
link: string,
items: any
}>()
const togo = (url: string) => {
if (!url) url = ''
window.location.href = normalizeLink(url)
}
</script>
<template>
<div>
<div class="rounded-md w-full py-1 px-4 border-2 border-transparent
hover:border-brand transition-colors"
@click="togo(link)">
{{text}}
</div>
</div>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import VTSidebarLink from './VTSidebarLink.vue'
import { useSidebar } from '../../composables/sidebar.js'
const { sidebar } = useSidebar()
</script>
<template>
<div class="VTSidebarTop">
<div class="rounded-vt shadow-vt overflow-hidden bg-cbg">
<div class="h-20 px-6 py-3 bg-brand">
<p class="font-bold py-1">公告</p>
<p class="text-sm">暂无</p>
</div>
<div class="h-60 px-6 py-4">
<div v-for="s in sidebar" :key="s.text">
<VTSidebarLink
:text="s.text"
:items="s.items"
:link="s.link" />
</div>
</div>
</div>
</div >
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,66 @@
<template>
<button class="VPSwitch" type="button" role="switch">
<span class="check">
<span class="icon" v-if="$slots.default">
<slot />
</span>
</span>
</button>
</template>
<style scoped>
.VPSwitch {
position: relative;
border-radius: 11px;
display: block;
width: 40px;
height: 40px;
flex-shrink: 0;
/* border: 1px solid var(--vp-c-divider); */
/* background-color: var(--vp-c-bg-mute); */
transition: border-color 0.25s, background-color 0.25s;
}
.VPSwitch:hover {
/* border-color: var(--vp-c-gray); */
}
.check {
position: absolute;
top: 5px;
left: 5px;
width: 30px;
height: 30px;
border-radius: 50%;
/* background-color: var(--vp-c-white);
box-shadow: var(--vp-shadow-1); */
transition: background-color 0.25s, transform 0.25s;
}
.dark .check {
/* background-color: var(--vp-c-black); */
}
.icon {
position: relative;
display: block;
width: 30px;
height: 30px;
border-radius: 50%;
overflow: hidden;
}
.icon :deep(svg) {
position: absolute;
top: 3px;
left: 3px;
width: 24px;
height: 24px;
fill: var(--vp-c-text-2);
}
.dark .icon :deep(svg) {
fill: var(--vp-c-text-1);
transition: opacity 0.25s;
}
</style>

View File

@@ -0,0 +1,74 @@
<script lang="ts" setup>
import { APPEARANCE_KEY } from '../../shared.js'
import VPSwitch from './VPSwitch.vue'
import VPIconSun from '../icons/VPIconSun.vue'
import VPIconMoon from '../icons/VPIconMoon.vue'
const toggle = typeof localStorage !== 'undefined' ? useAppearance() : () => {}
function useAppearance() {
const query = window.matchMedia('(prefers-color-scheme: dark)')
const { classList } = document.documentElement
let userPreference = localStorage.getItem(APPEARANCE_KEY) || 'auto'
let isDark = userPreference === 'auto'
? query.matches
: userPreference === 'dark'
query.onchange = (e) => {
if (userPreference === 'auto') {
setClass((isDark = e.matches))
}
}
function toggle() {
setClass((isDark = !isDark))
userPreference = isDark
? query.matches ? 'auto' : 'dark'
: query.matches ? 'light' : 'auto'
localStorage.setItem(APPEARANCE_KEY, userPreference)
}
function setClass(dark: boolean): void {
classList[dark ? 'add' : 'remove']('dark')
}
return toggle
}
</script>
<template>
<VPSwitch
class="VPSwitchAppearance"
aria-label="toggle dark mode"
@click="toggle"
>
<VPIconSun class="sun" />
<VPIconMoon class="moon" />
</VPSwitch>
</template>
<style scoped>
.sun {
opacity: 1;
}
.moon {
opacity: 0;
}
.dark .sun {
opacity: 0;
}
.dark .moon {
opacity: 1;
}
.dark .VPSwitchAppearance :deep(.check) {
/* transform: translateX(18px); */
}
</style>

View File

@@ -0,0 +1,60 @@
import {
Ref, ref, watch, readonly, onUnmounted
} from 'vue'
interface UseFlyoutOptions {
el: Ref<HTMLElement | undefined>
onFocus?(): void
onBlur?(): void
}
export const focusedElement = ref<HTMLElement>()
let active = false
let listeners = 0
export function useFlyout(options: UseFlyoutOptions) {
const focus = ref(false)
if (typeof window !== 'undefined') {
!active && activateFocusTracking()
listeners++
const unwatch = watch(focusedElement, (el) => {
if (el === options.el.value || options.el.value?.contains(el as Node)) {
focus.value = true
options.onFocus?.()
} else {
focus.value = false
options.onBlur?.()
}
})
onUnmounted(() => {
unwatch()
listeners--
if (!listeners) {
deactivateFocusTracking()
}
})
}
return readonly(focus)
}
function activateFocusTracking() {
document.addEventListener('focusin', handleFocusIn)
active = true
focusedElement.value = document.activeElement as HTMLElement
}
function deactivateFocusTracking() {
document.removeEventListener('focusin', handleFocusIn)
}
function handleFocusIn() {
focusedElement.value = document.activeElement as HTMLElement
}

View File

@@ -0,0 +1,77 @@
import { useData } from 'vitepress'
export function useHomeBg() {
const { site, theme } = useData()
const { base } = site.value
const {
logoImg, bgImg
} = theme.value
let { bgColor, flashColor, bgOpacity } = theme.value
let baseUrl = base
let bgStyle = ''
let bgImgSrc = ''
let bgInnerOpacity = 0.3
// let ftStyle = 'rgba(255,255,255,0.8)'
if (base === '/' || base.endsWith('/')) {
baseUrl = base.substring(0, base.length - 1)
}
// hero logo
const homeLogoSrc = `${baseUrl}/${logoImg}`.replaceAll('//', '/')
// feat color check
// if (typeof featuresColor === 'string') {
// ftStyle = featuresColor
// } else if (typeof featuresColor === 'object') {
// if (featuresColor.length >= 2) {
// ftStyle = `
// linear-gradient(to right,
// ${featuresColor[0]},
// ${featuresColor[1]}
// )
// `
// }
// }
// 背景色默认黑色
if (bgColor === undefined) bgColor = '0,0,0'
// 背景图透明度
if (bgOpacity === undefined) bgOpacity = 0
if (flashColor === undefined) flashColor = ['0,0,0', '0,0,0']
if (typeof flashColor === 'string') flashColor = [flashColor, flashColor]
if (bgImg) {
bgImgSrc = `${baseUrl}/${bgImg}`.replaceAll('//', '/')
bgInnerOpacity = bgOpacity - 0.3 <= 0 ? 0 : bgOpacity - 0.3
bgStyle = `
-webkit-linear-gradient(top,
rgba(${bgColor},${bgOpacity}) 0%,
rgba(${bgColor},${bgInnerOpacity}) 20%,
rgba(${bgColor},${bgInnerOpacity}) 80%,
rgba(${bgColor},${bgOpacity}) 100%
),
-webkit-linear-gradient(left,
rgba(${bgColor},${bgOpacity}) 0%,
rgba(${bgColor},${bgInnerOpacity}) 20%,
rgba(${bgColor},${bgInnerOpacity}) 80%,
rgba(${bgColor},${bgOpacity}) 100%),
url(${bgImgSrc})
`
}
return {
homeLogoSrc,
bgStyle,
bgImgSrc
}
}
export function useHomeParallax() {
const { theme } = useData()
// const { base } = site.value
const { parallaxEnable } = theme.value
return {
parallaxEnable
}
}

View File

@@ -0,0 +1,69 @@
import type { DefaultTheme } from 'vitepress/theme'
import { ref, computed, watch } from 'vue'
import { useData, useRoute } from 'vitepress'
export function useNav() {
const isScreenOpen = ref(false)
function openScreen() {
isScreenOpen.value = true
window.addEventListener('resize', closeScreenOnTabletWindow)
}
function closeScreen() {
isScreenOpen.value = false
window.removeEventListener('resize', closeScreenOnTabletWindow)
}
function toggleScreen() {
isScreenOpen.value ? closeScreen() : openScreen()
}
/**
* Close screen when the user resizes the window wider than tablet size.
*/
function closeScreenOnTabletWindow() {
window.outerWidth >= 768 && closeScreen()
}
const route = useRoute()
watch(() => route.path, closeScreen)
return {
isScreenOpen,
openScreen,
closeScreen,
toggleScreen
}
}
export function useLanguageLinks() {
const { site, localePath, theme } = useData()
return computed(() => {
const { langs } = site.value
const localePaths = Object.keys(langs)
// one language
if (localePaths.length < 2) {
return null
}
const route = useRoute()
// intentionally remove the leading slash because each locale has one
const currentPath = route.path.replace(localePath.value, '')
const candidates = localePaths.map((localePath) => ({
text: langs[localePath].label,
link: `${localePath}${currentPath}`
}))
const selectText = theme.value.selectText || 'Languages'
return {
text: selectText,
items: candidates
} as DefaultTheme.NavItemWithChildren
})
}

View File

@@ -0,0 +1,77 @@
import {
computed, onMounted, onUnmounted, Ref, ref, watchEffect
} from 'vue'
import { useData, useRoute } from 'vitepress'
import { getSidebar } from '../support/sidebar.js'
export function useSidebar() {
const route = useRoute()
const { theme, frontmatter } = useData()
const isOpen = ref(false)
const sidebar = computed(() => {
const sidebarConfig = theme.value.sidebar
return sidebarConfig ? getSidebar(sidebarConfig, route.path) : []
})
const hasSidebar = computed(() => (
frontmatter.value.sidebar !== false
&& sidebar.value.length > 0
&& frontmatter.value.layout !== 'home'
&& frontmatter.value.layout !== 'doc'
))
function open() {
isOpen.value = true
}
function close() {
isOpen.value = false
}
function toggle() {
isOpen.value ? close() : open()
}
return {
isOpen,
sidebar,
hasSidebar,
open,
close,
toggle
}
}
/**
* a11y: cache the element that opened the Sidebar (the menu button) then
* focus that button again when Menu is closed with Escape key.
*/
export function useCloseSidebarOnEscape(
isOpen: Ref<boolean>,
close: () => void
) {
let triggerElement: HTMLButtonElement | undefined
watchEffect(() => {
triggerElement = isOpen.value
? (document.activeElement as HTMLButtonElement)
: undefined
})
onMounted(() => {
window.addEventListener('keyup', onEscape)
})
onUnmounted(() => {
window.removeEventListener('keyup', onEscape)
})
function onEscape(e: KeyboardEvent) {
if (e.key === 'Escape' && isOpen.value) {
close()
triggerElement?.focus()
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,32 @@
// vitepress
import 'vitepress/dist/client/theme-default/styles/fonts.css'
import 'vitepress/dist/client/theme-default/styles/vars.css'
import 'vitepress/dist/client/theme-default/styles/base.css'
import 'vitepress/dist/client/theme-default/styles/utils.css'
import 'vitepress/dist/client/theme-default/styles/components/custom-block.css'
import 'vitepress/dist/client/theme-default/styles/components/vp-code.css'
import 'vitepress/dist/client/theme-default/styles/components/vp-doc.css'
import 'vitepress/dist/client/theme-default/styles/components/vp-sponsor.css'
// common styles
import '../styles/index.scss'
import '../styles/rewrite/index.scss'
// icon
import '../styles/fa/font-awesome.css'
// tailwind
import '../styles/tailwind/d.css'
// blog styles
import './styles/blog.css'
import Layout from './layouts/Layout.vue'
import NotFound from './layouts/NotFound.vue'
const BlogTheme = {
Layout,
NotFound
}
export default BlogTheme

View File

@@ -0,0 +1,114 @@
<script setup lang="ts">
import { provide, onMounted, ref, computed, reactive } from 'vue'
import VPNav from 'vitepress/dist/client/theme-default/components/VPNav.vue'
// import VPNav from '../components/nav/VPNav.vue'
import VTFloat from '../components/VTFloat.vue'
import VPContent from '../components/VPContent.vue'
import VTBackground from '../components/VTBackground.vue'
import type { UseScrollReturn } from '@vueuse/core'
import { useScriptTag } from '@vueuse/core'
import { vScroll } from '@vueuse/components'
import { useSidebar, useCloseSidebarOnEscape } from '../composables/sidebar.js'
import { useHomeBg } from '../composables/homebg.js'
const { bgStyle, bgImgSrc } = useHomeBg()
const {
isOpen: isSidebarOpen,
// open: openSidebar,
close: closeSidebar,
} = useSidebar()
useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
provide('close-sidebar', closeSidebar)
const renderHomeBackground = () => {
// const ds = document.documentElement.style
// ds.setProperty('--vt-bg-light', lightStyle)
const vpHome = document.getElementById('VTLayoutScroll')
const hs = vpHome.style
hs.backgroundSize = 'cover'
hs.backgroundAttachment = 'fixed'
hs.backgroundPosition = 'center center'
hs.backgroundImage = bgStyle
}
// const handleScroll = () => {
// const scrollTop = window.pageYOffset
// || document.documentElement.scrollTop
// || document.body.scrollTop
// if (scrollTop <= 100) navOpacity.value = scrollTop / 100
// }
onMounted(() => {
renderHomeBackground()
// window.addEventListener('scroll', handleScroll)
})
const navOpacity = ref(0)
function onScroll(state: UseScrollReturn) {
const arrivedTop = state.arrivedState.top
const val = state.y.value / 200
if (arrivedTop) navOpacity.value = 0
else navOpacity.value <= 1 ? (navOpacity.value = val) : 1
}
useScriptTag(
'/js/leaf.js',
(el: HTMLScriptElement) => {
},
)
</script>
<template>
<div class="Layout">
<slot name="layout-top" />
<!-- <VPSkipLink /> -->
<!-- <VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar" /> -->
<VPNav
class="transition-opacity duration-1000"
:style="[{ opacity: navOpacity }]" />
<!-- <VPLocalNav :open="isSidebarOpen" @open-menu="openSidebar" /> -->
<!-- <VPSidebar :open="isSidebarOpen" /> -->
<div
id="VTLayoutScroll"
class="h-screen overflow-scroll"
v-scroll="[onScroll, { throttle: 10 }]">
<VTBackground>
<VPContent class="h-full relative z-10" />
</VTBackground>
</div>
<!-- <VPContent>
<template #home-hero-before><slot name="home-hero-before" /></template>
<template #home-hero-after><slot name="home-hero-after" /></template>
<template #home-features-before><slot name="home-features-before" /></template>
<template #home-features-after><slot name="home-features-after" /></template>
<template #doc-before><slot name="doc-before" /></template>
<template #doc-after><slot name="doc-after" /></template>
<template #aside-top><slot name="aside-top" /></template>
<template #aside-bottom><slot name="aside-bottom" /></template>
<template #aside-outline-before><slot name="aside-outline-before" /></template>
<template #aside-outline-after><slot name="aside-outline-after" /></template>
<template #aside-ads-before><slot name="aside-ads-before" /></template>
<template #aside-ads-after><slot name="aside-ads-after" /></template>
</VPContent> -->
<VTFloat />
<slot name="layout-bottom" />
</div>
</template>
<style scoped>
.Layout {
display: flex;
flex-direction: column;
min-height: 100vh;
position: relative;
}
</style>

View File

@@ -0,0 +1,94 @@
<script setup lang="ts">
import { useData } from 'vitepress'
const { site } = useData()
const msgs = [
'There\'s nothing here.',
'How did we get here?',
'That\'s a Four-Oh-Four.',
'Looks like we\'ve got some broken links.'
]
function getMsg() {
return msgs[Math.floor(Math.random() * msgs.length)]
}
</script>
<template>
<div class="NotFound">
<p class="code">404</p>
<h1 class="title">PAGE NOT FOUND</h1>
<div class="divider"></div>
<blockquote class="quote">
{{ getMsg() }}
</blockquote>
<div class="action">
<a class="link" :href="site.base" aria-label="go to home">
Take me home
</a>
</div>
</div>
</template>
<style scoped>
.NotFound {
padding: 64px 24px 96px;
text-align: center;
}
@media (min-width: 768px) {
.NotFound {
padding: 96px 32px 168px;
}
}
.code {
line-height: 64px;
font-size: 64px;
font-weight: 600;
}
.title {
padding-top: 12px;
letter-spacing: 2px;
line-height: 20px;
font-size: 20px;
font-weight: 700;
}
.divider {
margin: 24px auto 18px;
width: 64px;
height: 1px;
background-color: var(--vp-c-divider)
}
.quote {
margin: 0 auto;
max-width: 256px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.action {
padding-top: 20px;
}
.link {
display: inline-block;
border: 1px solid var(--vp-c-brand);
border-radius: 16px;
padding: 3px 16px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-brand);
transition: border-color 0.25s, color .25s;
}
.link:hover {
border-color: var(--vp-c-brand-dark);
color: var(--vp-c-brand-dark);
}
</style>

View File

@@ -0,0 +1,11 @@
export const EXTERNAL_URL_RE = /^https?:/i
export const APPEARANCE_KEY = 'vitepress-theme-appearance'
export const inBrowser = typeof window !== 'undefined'
export const notFoundPageData = {
relativePath: '',
title: '404',
description: 'Not Found',
headers: [],
frontmatter: {},
lastUpdated: 0
}

View File

@@ -0,0 +1,16 @@
:root {
--vt-radius: 0.5rem;
/* sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
'2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
'3xl': '0 35px 60px -15px rgba(0, 0, 0, 0.3)',*/
/* --vt-shadow: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)'; */
/* --vt-shadow: 'none'; */
--vt-shadow: 0 35px 60px -15px rgba(0, 0, 0, 0.3);
}
body {
cursor: url(https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/Hexo/img/default.cur), default;
}

View File

@@ -0,0 +1,42 @@
import type { DefaultTheme } from 'vitepress/theme'
import { ensureStartingSlash } from './utils.js'
/**
* Get the `Sidebar` from sidebar option. This method will ensure to get correct
* sidebar config from `MultiSideBarConfig` with various path combinations such
* as matching `guide/` and `/guide/`. If no matching config was found, it will
* return empty array.
*/
export function getSidebar(
sidebar: DefaultTheme.Sidebar,
path: string
): DefaultTheme.SidebarGroup[] {
if (Array.isArray(sidebar)) {
return sidebar
}
path = ensureStartingSlash(path)
for (const dir in sidebar) {
// make sure the multi sidebar key starts with slash too
if (path.startsWith(ensureStartingSlash(dir))) {
return sidebar[dir]
}
}
return []
}
export function getFlatSideBarLinks(
sidebar: DefaultTheme.SidebarGroup[]
): DefaultTheme.SidebarItem[] {
const links: DefaultTheme.SidebarItem[] = []
for (const group of sidebar) {
for (const link of group.items) {
links.push(link)
}
}
return links
}

View File

@@ -0,0 +1,90 @@
/** @format */
import { ref } from 'vue'
import { withBase } from 'vitepress'
import { EXTERNAL_URL_RE } from '../shared.js'
export const HASH_RE = /#.*$/
export const EXT_RE = /(index)?\.(md|html)$/
const inBrowser = typeof window !== 'undefined'
const hashRef = ref(inBrowser ? location.hash : '')
export function isExternal(path: string): boolean {
return EXTERNAL_URL_RE.test(path)
}
export function throttleAndDebounce(fn: () => void, delay: number): () => void {
let timeout: any
let called = false
return () => {
if (timeout) {
clearTimeout(timeout)
}
if (!called) {
fn()
called = true
setTimeout(() => {
called = false
}, delay)
} else {
timeout = setTimeout(fn, delay)
}
}
}
export function isActive(
currentPath: string,
matchPath?: string,
asRegex: boolean = false
): boolean {
if (matchPath === undefined) {
return false
}
currentPath = normalize(`/${currentPath}`)
if (asRegex) {
return new RegExp(matchPath).test(currentPath)
}
if (normalize(matchPath) !== currentPath) {
return false
}
const hashMatch = matchPath.match(HASH_RE)
if (hashMatch) {
return hashRef.value === hashMatch[0]
}
return true
}
export function ensureStartingSlash(path: string): string {
return /^\//.test(path) ? path : `/${path}`
}
export function normalize(path: string): string {
return decodeURI(path).replace(HASH_RE, '').replace(EXT_RE, '')
}
export function normalizeLink(url: string): string {
if (isExternal(url)) {
return url
}
const { pathname, search, hash } = new URL(url, 'http://example.com')
const normalizedPath = pathname.endsWith('/') || pathname.endsWith('.html')
? url
: `${pathname.replace(/(\.md)?$/, '.html')}${search}${hash}`
return withBase(normalizedPath)
}
export function fmt(inputTime: string) {
return inputTime.replace(/T/g, ' ').replace(/\.[\d]{3}Z/, '')
}