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

158
packages/vuetom/README.md Normal file
View File

@@ -0,0 +1,158 @@
<p align="center"><a href="https://gitee.com/lauset/vitepress-theme-vuetom" target="_blank" rel="noopener noreferrer"><img width="180" src="https://cdn.jsdelivr.net/gh/lauset/vitepress-theme-vuetom/packages/docs/public/logo/vuetom-logo.png" alt="logo"></a></p>
<p align="center">
<a href="https://github.com/xugaoyi/vuepress-theme-vdoing/blob/master/LICENSE"><img src="https://img.shields.io/github/license/xugaoyi/vuepress-theme-vdoing
" alt="License"></a>
<a href="https://www.npmjs.com/package/vitepress-theme-vuetom"><img alt="npm" src="https://img.shields.io/npm/v/vitepress-theme-vuetom"></a>
</p>
<h2 align="center">vitepress-theme-vuetom</h2>
## 简要说明
1. 为 vitepress 1.x 提供的主题
2. 提供文档与博客两种风格的主题
[**更新日志**](CHANGELOG.md)
## 主题预览
* [**文档**风格 (*github.io*)](https://lauset.github.io/vitepress-theme-vuetom/)
* [**文档**风格 (*vercel.app*)](https://vitepress-theme-vuetom.vercel.app/vt/)
* [**博客**风格 (*开发中*)](https://vitepress-theme-vuetom-blog.vercel.app/myblog/)
## 快速上手
* 拉取项目
```bash
git clone https://github.com/lauset/vitepress-theme-vuetom.git
cd vitepress-theme-vuetom
```
* 安装依赖
```bash
pnpm install
```
* 启动文档示例
```bash
# 根目录运行
pnpm dev:docs
# 文档目录运行
cd packages/docs
pnpm dev
```
* 启动博客示例
```bash
# 根目录运行
pnpm dev:blog
# 博客目录运行
cd packages/blog
pnpm dev
```
* 根目录构建操作
```bash
# 清理已打包的文件
pnpm clean:all
# 构建主题
pnpm build:theme
# 构建文档
pnpm build:docs
# 构建博客
pnpm build:blog
```
* 根目录预览操作
```bash
# 预览文档
pnpm preview:docs
# 预览博客
pnpm preview:blog
```
* 发布主题包
```bash
cd packages/vuetom
pnpm pub
```
## 文档目录多语言
查看项目文件列表
```shell
crowdin:list
```
上传待翻译的源文件
```shell
crowdin:upload
```
查看预下载文件列表
```shell
crowdin:dryrun
```
下载en-US翻译文件
```shell
crowdin:us
```
下载zh-TW翻译文件
```shell
crowdin:tw
```
## 简单展示
![首页](/resources/pic01.png)
![暗黑主题](/resources/pic02.png)
![语法示例](/resources/pic03.png)
![其他](/resources/pic04.png)
## 仓库地址
[github](https://github.com/lauset/vitepress-theme-vuetom)
[gitee](https://gitee.com/lauset/vitepress-theme-vuetom)
## 感谢
[Vue](https://vuejs.org/)
[Vite](https://cn.vitejs.dev/)
[Vitepress](https://vitepress.vuejs.org/)
[Vercel](https://vercel.com/docs)
[ElementPlus](https://element-plus.gitee.io/zh-CN/)
[Crowdin](https://crowdin.com/)

4
packages/vuetom/blog.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
import { Theme } from 'vitepress'
declare const BlogTheme: Theme
export default BlogTheme

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/, '')
}

View File

@@ -0,0 +1,82 @@
<script setup lang="ts">
import { onMounted, onUnmounted, watch, nextTick } from 'vue'
import DefaultTheme from 'vitepress/theme'
import { useData, useRoute } from 'vitepress'
import {
useFlash,
useBackground,
useFeatures,
useHeroMove,
} from '../composables/home.js'
const { Layout } = DefaultTheme
// const route = useRoute()
const { site, theme } = useData()
const { base} = site.value
const { logoImg } = theme.value
const { flashEnable, flashStyle } = useFlash()
const { bgEnable, bgStyle } = useBackground()
const { ftStyle } = useFeatures()
const { parallaxEnable, heroMove } = useHeroMove()
const pageBgEnable = theme.value.pageBgEnable || true
const pageBgOpacity = theme.value.pageBgOpacity || 0.8
const logoSrc = `${base}${logoImg}`.replaceAll('//', '/')
const renderHomeBg = () => {
const domStyle = document.documentElement.style
// 首页闪烁动画样式设置
if (flashEnable && flashStyle) {
domStyle.setProperty('--vt-bg-light', flashStyle)
}
// 文章背景图设置
if (pageBgEnable && pageBgOpacity) {
domStyle.setProperty('--vt-bg-doc', `rgba(var(--vt-c-bg-rgb), ${pageBgOpacity})`)
}
// 首页背景图设置
if (bgEnable && bgStyle) {
domStyle.setProperty('--vt-bg-content', bgStyle)
}
if (ftStyle) {
const vpFeat: any = document.getElementsByClassName('VPFeatures')[0]
const fs = vpFeat?.style
if (fs) fs.background = ftStyle
}
}
// const checkPath = (path: string) => {
// const homePaths = [base, `${base}index.html`, `${base}${lang}/`]
// const isHome = homePaths.includes(path)
// if (isHome) {
// nextTick(() => {
// renderHomeBg()
// })
// }
// }
// watch(
// () => route.path,
// (path) => {
// checkPath(path)
// }
// )
onMounted(() => {
renderHomeBg()
if (parallaxEnable) window.addEventListener('mousemove', heroMove)
})
onUnmounted(() => {
if (parallaxEnable) window.removeEventListener('mousemove', heroMove)
})
</script>
<template>
<Layout>
<template #home-hero-before>
<img class="VPHeroLogo" :src="logoSrc" />
<slot name="home-hero-before" />
</template>
</Layout>
</template>

View File

@@ -0,0 +1,144 @@
import { useData } from 'vitepress'
export function useFlash() {
const { site, theme } = useData()
const { base } = site.value
let { flashEnable, flashColor, bgImg } = theme.value
if (flashColor === undefined) flashColor = ['0,0,0', '0,0,0']
if (typeof flashColor === 'string') flashColor = [flashColor, flashColor]
let flashStyle = ''
if (bgImg && flashEnable) {
const bgImgSrc = (`${base}${bgImg}`).replaceAll('//', '/')
flashStyle = `
-webkit-linear-gradient(top,
rgba(${flashColor[0]}, 0.8) 0%,
rgba(${flashColor[0]}, 0.2) 20%,
rgba(${flashColor[0]}, 0) 80%,
rgba(${flashColor[0]}, 0) 100%
),
-webkit-linear-gradient(left,
rgba(${flashColor[1]}, 0) 0%,
rgba(${flashColor[1]}, 0) 20%,
rgba(${flashColor[1]}, 0.2) 80%,
rgba(${flashColor[1]}, 0.8) 100%),
url(${bgImgSrc})
`
}
return {
flashEnable,
flashStyle,
}
}
export function useBackground() {
const { site, theme } = useData()
const { base } = site.value
let { bgImg, bgColor, bgOpacity } = theme.value
let bgEnable = false
let bgStyle = ''
let bgInnerOpacity = 0.3
if (bgImg) bgEnable = true
if (bgColor === undefined) bgColor = '0,0,0'
if (bgOpacity === undefined) bgOpacity = 0.6
bgInnerOpacity = bgOpacity - 0.3 <= 0 ? 0 : bgOpacity - 0.3
if (bgEnable) {
const bgImgSrc = (`${base}${bgImg}`).replaceAll('//', '/')
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 {
bgEnable,
bgStyle,
}
}
export function useFeatures() {
const { theme } = useData()
let { featuresColor } = theme.value
let ftStyle = 'rgba(255,255,255,0.8)'
if (typeof featuresColor === 'string') {
ftStyle = featuresColor
} else if (typeof featuresColor === 'object') {
if (featuresColor.length >= 2) {
ftStyle = `
linear-gradient(to right,
${featuresColor[0]},
${featuresColor[1]}
)
`
}
}
return {
ftStyle,
}
}
export function useHeroMove() {
const { theme } = useData()
const { parallaxEnable } = theme.value
function heroMove(e: any) {
document
.querySelectorAll(
`
.VPHomeHero .name,
.VPHomeHero .text,
.VPHomeHero .tagline,
.VPHomeHero .VPButton,
.VPHome .VPHomeFeatures
`
)
.forEach((h: Element) => {
const hd: any = h
const speed: any = hd.getAttribute('data-speed') || 10
let x = (window.innerWidth - e.pageX * speed) / 100
let y = (window.innerHeight - e.pageY * speed) / 100
switch (hd.className.substring(0, 3).toUpperCase()) {
case 'VPF':
x /= 8
y /= 8
break
case 'TAG':
x /= 6
y /= 6
break
case 'TEX':
x /= 4
y /= 4
break
case 'VPB':
x /= 2
y /= 2
break
case 'NAM':
x /= 1
y /= 1
break
default:
break
}
const hds = hd?.style
if (hds) {
hds.transform = `translateX(${x}px) translateY(${y}px)`
hds.transition = 'transform 0.2s ease-out'
}
})
}
return {
parallaxEnable,
heroMove,
}
}

View File

@@ -0,0 +1,18 @@
// vitepress styles ==> /dist/client/theme-default/styles
import vitepressTheme from 'vitepress/theme'
// for dev (prod should import .css)
import '../styles/index.scss'
import '../styles/rewrite/index.scss'
import { Theme } from 'vitepress'
import VTLayout from './components/VTLayout.vue'
const DocsTheme: Theme = {
...vitepressTheme,
Layout: VTLayout
}
export * from 'vitepress/theme'
export default DocsTheme

5
packages/vuetom/docs.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
import { Theme } from 'vitepress'
declare const DocsTheme: Theme
export * from 'vitepress/theme'
export default DocsTheme

View File

@@ -0,0 +1,99 @@
import path from 'path'
import {
src, dest, series, parallel
} from 'gulp'
import gulpSass from 'gulp-sass'
import dartSass from 'sass'
import autoprefixer from 'gulp-autoprefixer'
import cleanCSS from 'gulp-clean-css'
import rename from 'gulp-rename'
const distFolder = path.resolve(__dirname, './dist/styles')
const distRewriteFolder = path.resolve(__dirname, './dist/styles/rewrite')
function buildRewriteStyles() {
const sass = gulpSass(dartSass)
const noPrefixFile = /(index|main)/
return src(path.resolve(__dirname, 'styles/rewrite/*.scss'))
.pipe(sass.sync())
.pipe(
autoprefixer({
cascade: true // 是否美化属性值 默认 true
})
)
.pipe(
cleanCSS(
{
compatibility: 'ie9', // 压缩兼容模式IE9
format: 'beautify'
},
(details) => {
console.log(
`${details.name}: ${details.stats.originalSize / 1000} KB -> ${
details.stats.minifiedSize / 1000
} KB`
)
}
)
)
.pipe(
rename((file) => {
if (!noPrefixFile.test(file.basename)) {
file.basename = `vt-${file.basename}`
}
})
)
.pipe(dest(distRewriteFolder))
}
function buildCommonStyles() {
const sass = gulpSass(dartSass)
const noPrefixFile = /(index|main)/
return src(path.resolve(__dirname, 'styles/*.scss'))
.pipe(sass.sync())
.pipe(autoprefixer({ cascade: true }))
.pipe(
cleanCSS(
{
compatibility: 'ie9',
format: 'beautify'
},
(details) => {
console.log(
`${details.name}: ${details.stats.originalSize / 1000} KB -> ${
details.stats.minifiedSize / 1000
} KB`
)
}
)
)
.pipe(
rename((file) => {
if (!noPrefixFile.test(file.basename)) {
file.basename = `vt-${file.basename}`
}
})
)
.pipe(dest(distFolder))
}
/**
* 移动图标、字体、tailwind样式文件至dist对应目录下
*/
function copyOtherResource() {
return src([
path.resolve(__dirname, 'styles/fa'),
path.resolve(__dirname, 'styles/fonts'),
path.resolve(__dirname, 'styles/tailwind')
]).pipe(
dest(distFolder)
)
}
export const build = parallel(
buildRewriteStyles,
buildCommonStyles,
copyOtherResource
)
export default build

4
packages/vuetom/index.ts Normal file
View File

@@ -0,0 +1,4 @@
import DocsTheme from './doc/index.js'
export * from './doc/index.js'
export default DocsTheme

View File

@@ -0,0 +1,82 @@
{
"name": "vitepress-theme-vuetom",
"version": "2.3.0",
"description": "A Vitepress Theme, Have blog and document styles",
"type": "module",
"packageManager": "pnpm@7.9.0",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./types/index.d.ts",
"exports": {
".": {
"types": "./types/index.d.ts",
"import": "./dist/index.js"
},
"./dist/*": "./dist/*",
"./package.json": "./package.json",
"./docs": {
"types": "./docs.d.ts",
"default": "./dist/doc/index.js"
},
"./blog": {
"types": "./blog.d.ts",
"default": "./dist/blog/index.js"
}
},
"scripts": {
"pub": "npm publish",
"clean": "rimraf ./dist",
"build": "pnpm clean && pnpm tailwind:css && pnpm build:scss && pnpm build:vt",
"build:vt": "tsc -p . && cross-env NODE_ENV=build node ../../scripts/build-vt",
"build:scss": "gulp --require @esbuild-kit/cjs-loader -f gulpfile.ts",
"tailwind:css": "npx tailwindcss -i ./styles/tailwind/s.css -o ./styles/tailwind/d.css",
"tailwind:watch": "npx tailwindcss -i ./styles/tailwind/s.css -o ./styles/tailwind/d.css --watch"
},
"engines": {
"node": ">=16.0.0"
},
"files": [
"dist",
"types",
"blog.d.ts",
"docs.d.ts",
"package.json",
"README.md"
],
"keywords": [
"vitepress",
"theme"
],
"author": "lauset",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/lauset/vitepress-theme-vuetom.git"
},
"bugs": {
"url": "https://github.com/lauset/vitepress-theme-vuetom/issues"
},
"homepage": "https://github.com/lauset/vitepress-theme-vuetom#readme",
"devDependencies": {
"@esbuild-kit/cjs-loader": "^2.2.1",
"@types/nprogress": "^0.2.0",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.19",
"tailwindcss": "^3.2.4"
},
"dependencies": {
"@docsearch/css": "^3.3.0",
"@docsearch/js": "^3.3.0",
"@vueuse/components": "^9.6.0",
"@vueuse/core": "^9.6.0",
"body-scroll-lock": "^4.0.0-beta.0",
"nprogress": "^0.2.0",
"shiki": "^0.11.1",
"vite": "^3.2.5",
"vitepress": "1.0.0-alpha.30",
"vue": "^3.2.45"
},
"peerDependencies": {
"vitepress": "1.0.0-alpha.30"
}
}

View File

@@ -0,0 +1,7 @@
/* eslint-disable global-require */
module.exports = {
plugins: [
require('postcss-import'),
require('tailwindcss')
]
}

View File

@@ -0,0 +1,136 @@
/** code macos */
div[class*='language-'] {
&.macos,&.light {
margin: 0 0 24px;
border-radius: 7px;
box-shadow: var(--vp-shadow-4);
&:hover {
.line-numbers-wrapper {
color: var(--vp-c-brand);
font-weight: 600;
opacity: 1;
}
}
.line-numbers-wrapper {
padding-top: 40px;
width: 32px;
border-right: 0px;
opacity: 0.3;
}
&::before {
position: initial;
display: block;
color: var(--vp-c-text-2);
background: var(--vp-c-bg-soft);
height: 24px;
line-height: 24px;
font-weight: bold;
text-align: center;
}
&::after {
position: absolute;
top: 8px;
left: 8px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #fc625d;
-webkit-box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
content: ' ';
}
pre {
code {
padding-left: 32px;
font-weight: 600;
}
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #747474;
}
.token.punctuation {
color: #743c3c;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #1a5997;
}
.token.boolean,
.token.number,
.token.function {
color: #af4b09;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #a0710b;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #a241a3;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #1e8545;
}
.token.operator,
.token.entity,
.token.url {
color: #168d8b;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}
}
}
.dark {
div[class*='language-'] {
&.macos {
&::before {
background: var(--vp-c-bg);
}
.lang {
background-color: transparent;
display: none;
}
}
}
}

View File

@@ -0,0 +1,260 @@
/** code default */
code .token.deleted {
color: #ec5975;
}
code .token.inserted {
color: var(--c-brand);
}
div[class*='language-'] {
position: relative;
background-color: var(--vp-c-bg-soft);
transition: color 0.5s;
border: 2px solid transparent;
overflow-x: auto;
.line-numbers-wrapper {
border-right: 2px solid var(--vp-c-brand);
transition: opacity 0.5s;
opacity: 0.3;
}
}
div[class*='language-']:hover {
border: 2px solid var(--vp-c-brand);
.line-numbers-wrapper {
color: var(--vp-c-brand);
font-weight: 600;
opacity: 1;
}
&::before {
opacity: 0;
}
&::after {
width: 100%;
opacity: 1;
}
}
div[class*='language-']::after {
content: '';
height: 5px;
width: 0;
background: var(--vp-c-brand);
transition: all 0.5s;
opacity: 0.3;
display: block;
}
div[class*='language-']::before {
position: absolute;
bottom: 0;
font-size: 0.8rem;
color: #888;
height: 20px;
line-height: 20px;
width: 100%;
text-align: center;
transition: opacity 0.5s;
display: none;
}
.dark {
div[class*='language-'] {
background-color: var(--vp-c-bg-alt);
}
}
@media (min-width: 420px) {
div[class*='language-'] {
margin: 1rem 0;
border-radius: 6px;
}
li > div[class*='language-'] {
margin: 1rem 0 1rem 0rem;
border-radius: 6px;
}
}
div[class~='language-html']:before,
div[class~='language-markup']:before {
content: 'html';
}
div[class~='language-md']:before,
div[class~='language-markdown']:before {
content: 'md';
}
div[class~='language-css']:before {
content: 'css';
}
div[class~='language-sass']:before {
content: 'sass';
}
div[class~='language-scss']:before {
content: 'scss';
}
div[class~='language-less']:before {
content: 'less';
}
div[class~='language-stylus']:before {
content: 'styl';
}
div[class~='language-js']:before,
div[class~='language-javascript']:before {
content: 'js';
}
div[class~='language-ts']:before,
div[class~='language-typescript']:before {
content: 'ts';
}
div[class~='language-json']:before {
content: 'json';
}
div[class~='language-rb']:before,
div[class~='language-ruby']:before {
content: 'rb';
}
div[class~='language-py']:before,
div[class~='language-python']:before {
content: 'py';
}
div[class~='language-sh']:before,
div[class~='language-bash']:before {
content: 'sh';
}
div[class~='language-php']:before {
content: 'php';
}
div[class~='language-go']:before {
content: 'go';
}
div[class~='language-rust']:before {
content: 'rust';
}
div[class~='language-java']:before {
content: 'java';
}
div[class~='language-c']:before {
content: 'c';
}
div[class~='language-yaml']:before {
content: 'yaml';
}
div[class~='language-dockerfile']:before {
content: 'dockerfile';
}
div[class~='language-vue']:before {
content: 'vue';
}
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML.
* Based on https://github.com/chriskempson/tomorrow-theme
*
* @author Rose Pritchard
*/
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}
/* pre code */
.container pre {
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
padding: 15px;
margin-bottom: 0;
overflow: auto;
}

View File

@@ -0,0 +1,78 @@
:root {
/**
* Base Colors
* --------------------------------------------------------------------- */
--c-white-rgb: 255,255,255;
--c-black-rgb: 0,0,0;
--c-white: #ffffff;
--c-white-dark: #f8f8f8;
--c-black: #000000;
--c-divider-light: rgba(60, 60, 67, 0.12);
--c-divider-dark: rgba(84, 84, 88, 0.48);
--vt-c-white: #ffffff;
--vt-c-white-soft: #f9f9f9;
--vt-c-white-mute: #f1f1f1;
--vt-c-black: #1a1a1a;
--vt-c-black-pure: #000000;
--vt-c-black-soft: #242424;
--vt-c-black-mute: #2f2f2f;
--vt-c-indigo: #213547;
--vt-c-indigo-soft: #476582;
--vt-c-indigo-light: #aac8e4;
--vt-c-gray: #8e8e8e;
--vt-c-gray-light-1: #aeaeae;
--vt-c-gray-light-2: #c7c7c7;
--vt-c-gray-light-3: #d1d1d1;
--vt-c-gray-light-4: #e5e5e5;
--vt-c-gray-light-5: #f2f2f2;
--vt-c-gray-dark-1: #636363;
--vt-c-gray-dark-2: #484848;
--vt-c-gray-dark-3: #3a3a3a;
--vt-c-gray-dark-4: #282828;
--vt-c-gray-dark-5: #202020;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.70);
--vt-c-text-light-3: rgba(60, 60, 60, 0.33);
--vt-c-text-light-4: rgba(60, 60, 60, 0.18);
--vt-c-text-light-code: var(--vt-c-indigo-soft);
--vt-c-text-dark-1: rgba(255, 255, 255, 0.87);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.60);
--vt-c-text-dark-3: rgba(235, 235, 235, 0.38);
--vt-c-text-dark-4: rgba(235, 235, 235, 0.18);
--vt-c-text-dark-code: var(--vt-c-indigo-light);
--vp-c-blue: #16a0db;
--vp-c-blue-light: #25beff;
--vp-c-blue-lighter: #5cceff;
--vp-c-blue-dark: #198aba;
--vp-c-blue-darker: #13678a;
--vp-c-blue-dimm-1: rgba(66, 145, 184, 0.5);
--vp-c-blue-dimm-2: rgba(66, 145, 184, 0.4);
--vp-c-blue-dimm-3: rgba(66, 145, 184, 0.3);
--vp-c-blue-dimm-4: rgba(66, 147, 184, 0.2);
--vp-c-blue-dimm-5: rgba(66, 139, 184, 0.1);
--vp-c-pink: #e44a8a;
--vp-c-pink-light: #f962a1;
--vp-c-pink-lighter: #ff91bf;
--vp-c-pink-dark: #b31858;
--vp-c-pink-darker: #720e37;
--vp-c-pink-dimm-1: rgba(184, 66, 127, 0.5);
--vp-c-pink-dimm-2: rgba(184, 66, 100, 0.25);
--vp-c-pink-dimm-3: rgba(184, 66, 113, 0.05);
}

View File

@@ -0,0 +1,58 @@
:root {
// custom-block.tip
--vp-custom-block-tip-border: var(--vp-c-brand-light);
--vp-custom-block-tip-text: var(--vp-c-brand);
--vp-custom-block-tip-bg: transparent;
// custom-block.warning
--vp-custom-block-warning-border: #b99a02;
--vp-custom-block-warning-text: #b49704;
--vp-custom-block-warning-bg: #e7c00010;
// custom-block.danger
--vp-custom-block-danger-border: #b90e0e;
--vp-custom-block-danger-text: #cf0b0b;
--vp-custom-block-danger-bg: #cc000010;
// custom-block.info
--vp-custom-block-info-border: #05b640;
--vp-custom-block-info-text: #05b740;
--vp-custom-block-info-bg: #06ce4910;
}
.custom-block.tip,
.custom-block.info,
.custom-block.warning,
.custom-block.danger {
margin: 1rem 0;
border-width: 2px;
border-left: 0.5rem solid;
border-radius: 0.5rem;
padding: 0.5rem 1.3rem;
overflow-x: auto;
&:hover {
transition: all 0.5s;
box-shadow: var(--vp-shadow-4);
border-left: 2px solid;
}
a {
cursor: pointer;
}
.custom-block-title {
font-weight: 600;
}
}
.custom-block.details {
margin: 1rem 0;
border-width: 2px;
border-radius: 0.5rem;
padding: 0.5rem 1.3rem;
summary {
outline: none;
cursor: pointer;
}
}
.dark {
.custom-block.tip {
background-color: #23232380;
}
}

2337
packages/vuetom/styles/fa/font-awesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More