73f4788d7e
- 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
319 lines
8.0 KiB
Vue
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>
|