Files
test-repo/docs/.vitepress/theme/Layout.vue
T
sanbuphy 73f4788d7e feat: comprehensive documentation and demo updates
- Update READMEs and docs across multiple languages
- Enhance interactive demos for Agent, LLM, VLM, Audio, Image Gen, Terminal, and Web Basics
- Add new appendix sections for Database and IDE intros
- Update VitePress config, theme, and utility scripts
- Clean up unused assets and components
2026-01-16 19:10:51 +08:00

319 lines
8.0 KiB
Vue

<script setup>
import DefaultTheme from 'vitepress/theme'
import { useData } from 'vitepress'
import TextType from './components/TextType.vue'
import GitHubStars from './components/GitHubStars.vue'
import { onMounted, ref, watch } from 'vue'
import { Setting } from '@element-plus/icons-vue'
const { frontmatter } = useData()
const homeTaglineTyping = {
typingSpeed: 45,
initialDelay: 0,
pauseDuration: 2500,
postDeletingDelay: 500,
deletingSpeed: 18
}
const FONT_SIZE_STORAGE_KEY = 'ev-doc-font-size'
const LINE_HEIGHT_STORAGE_KEY = 'ev-doc-line-height'
const MIN_FONT_SIZE = 12
const MAX_FONT_SIZE = 18
const DEFAULT_FONT_SIZE = 14
const MIN_LINE_HEIGHT = 1.25
const MAX_LINE_HEIGHT = 1.8
const DEFAULT_LINE_HEIGHT = 1.65
const fontSize = ref(DEFAULT_FONT_SIZE)
const lineHeight = ref(DEFAULT_LINE_HEIGHT)
const isHydrated = ref(false)
const clampFontSize = (value) => {
if (value === null || value === undefined || value === '')
return DEFAULT_FONT_SIZE
const numeric = Number(value)
if (!Number.isFinite(numeric)) return DEFAULT_FONT_SIZE
return Math.min(MAX_FONT_SIZE, Math.max(MIN_FONT_SIZE, numeric))
}
const clampLineHeight = (value) => {
if (value === null || value === undefined || value === '')
return DEFAULT_LINE_HEIGHT
const numeric = Number(value)
if (!Number.isFinite(numeric)) return DEFAULT_LINE_HEIGHT
return Math.min(MAX_LINE_HEIGHT, Math.max(MIN_LINE_HEIGHT, numeric))
}
const applyFontSize = (size) => {
if (typeof document === 'undefined') return
document.documentElement.style.setProperty('--ev-doc-font-size', `${size}px`)
}
const applyLineHeight = (value) => {
if (typeof document === 'undefined') return
document.documentElement.style.setProperty(
'--ev-doc-line-height',
String(value)
)
}
const decreaseFontSize = () => {
fontSize.value = clampFontSize(fontSize.value - 1)
}
const increaseFontSize = () => {
fontSize.value = clampFontSize(fontSize.value + 1)
}
const resetFontSize = () => {
fontSize.value = DEFAULT_FONT_SIZE
}
const resetLineHeight = () => {
lineHeight.value = DEFAULT_LINE_HEIGHT
}
onMounted(() => {
const saved = clampFontSize(localStorage.getItem(FONT_SIZE_STORAGE_KEY))
const savedLineHeight = clampLineHeight(
localStorage.getItem(LINE_HEIGHT_STORAGE_KEY)
)
fontSize.value = saved
lineHeight.value = savedLineHeight
applyFontSize(saved)
applyLineHeight(savedLineHeight)
isHydrated.value = true
})
watch(fontSize, (next) => {
if (!isHydrated.value) return
const normalized = clampFontSize(next)
applyFontSize(normalized)
localStorage.setItem(FONT_SIZE_STORAGE_KEY, String(normalized))
})
watch(lineHeight, (next) => {
if (!isHydrated.value) return
const normalized = clampLineHeight(next)
applyLineHeight(normalized)
localStorage.setItem(LINE_HEIGHT_STORAGE_KEY, String(normalized))
})
</script>
<template>
<DefaultTheme.Layout>
<template #nav-bar-content-after>
<GitHubStars />
<ClientOnly>
<el-popover placement="bottom-end" trigger="click" :width="260">
<template #reference>
<button
class="ev-fontsize-button"
type="button"
aria-label="阅读设置"
style="margin-left: 16px; padding: 0; width: 32px"
>
<el-icon :size="16"><Setting /></el-icon>
</button>
</template>
<div class="ev-fontsize-panel">
<div class="ev-setting-group">
<div class="ev-setting-header">
<div class="ev-setting-title">字号</div>
<div class="ev-setting-value">{{ fontSize }}px</div>
</div>
<div class="ev-fontsize-actions">
<button
class="ev-fontsize-action"
type="button"
@click="decreaseFontSize"
>
A-
</button>
<button
class="ev-fontsize-action"
type="button"
@click="resetFontSize"
>
默认
</button>
<button
class="ev-fontsize-action"
type="button"
@click="increaseFontSize"
>
A+
</button>
</div>
<el-slider
v-model="fontSize"
:min="MIN_FONT_SIZE"
:max="MAX_FONT_SIZE"
:step="1"
/>
</div>
<div class="ev-setting-group">
<div class="ev-setting-header">
<div class="ev-setting-title">行距</div>
<div class="ev-setting-value">{{ lineHeight.toFixed(2) }}</div>
</div>
<div class="ev-fontsize-actions">
<button
class="ev-fontsize-action"
type="button"
@click="resetLineHeight"
>
默认
</button>
<button
class="ev-fontsize-action"
type="button"
@click="lineHeight = clampLineHeight(lineHeight - 0.05)"
>
更紧
</button>
<button
class="ev-fontsize-action"
type="button"
@click="lineHeight = clampLineHeight(lineHeight + 0.05)"
>
更松
</button>
</div>
<el-slider
v-model="lineHeight"
:min="MIN_LINE_HEIGHT"
:max="MAX_LINE_HEIGHT"
:step="0.05"
/>
</div>
</div>
</el-popover>
</ClientOnly>
</template>
<template #home-hero-info-after>
<div
v-if="frontmatter.layout === 'home' && frontmatter.hero?.tagline"
class="vp-typed-tagline"
>
<ClientOnly>
<TextType
:text="frontmatter.hero.tagline"
v-bind="homeTaglineTyping"
:loop="true"
/>
</ClientOnly>
</div>
</template>
</DefaultTheme.Layout>
</template>
<style>
/* 隐藏默认的 tagline,因为我们用打字机效果替代了它 */
.VPHomeHero .tagline {
display: none !important;
}
/* 调整打字机容器的样式,使其看起来像原来的 tagline */
.vp-typed-tagline {
padding-top: 8px;
line-height: 28px;
font-size: 18px;
font-weight: 500;
white-space: pre-wrap;
color: var(--vp-c-text-2);
min-height: 28px;
display: flex;
/* 确保左对齐 */
text-align: left;
justify-content: flex-start;
}
@media (min-width: 640px) {
.vp-typed-tagline {
line-height: 32px;
font-size: 20px;
}
}
@media (min-width: 960px) {
.vp-typed-tagline {
line-height: 36px;
font-size: 24px;
}
}
.ev-fontsize-button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 32px;
min-width: 32px;
padding: 0 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 999px;
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 13px;
font-weight: 600;
line-height: 1;
cursor: pointer;
}
.ev-fontsize-button:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
.ev-fontsize-panel {
display: grid;
gap: 12px;
}
.ev-setting-group {
display: grid;
gap: 8px;
}
.ev-setting-header {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.ev-setting-title {
font-size: 13px;
font-weight: 600;
color: var(--vp-c-text-1);
}
.ev-setting-value {
font-size: 12px;
color: var(--vp-c-text-2);
}
.ev-fontsize-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.ev-fontsize-action {
height: 32px;
border: 1px solid var(--vp-c-divider);
border-radius: 10px;
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 13px;
cursor: pointer;
}
.ev-fontsize-action:hover {
border-color: var(--vp-c-brand);
color: var(--vp-c-brand);
}
</style>