feat(docs): add interactive demo components for technical appendices

Add placeholder Vue components for visualizing technical concepts across multiple domains including frontend routing, browser rendering, cache design, queue design, database principles, API design, cloud services, and backend evolution. These components provide interactive educational content for the documentation.

Update documentation structure to include new appendix sections and enhance existing content with visual components. Remove unused 'codex' dependency from package.json.
This commit is contained in:
sanbuphy
2026-02-06 03:34:50 +08:00
parent e8bba6f7c0
commit 7c70c37072
171 changed files with 69830 additions and 6689 deletions
@@ -1,290 +1,304 @@
<template>
<div class="latent-space-viz">
<el-card shadow="never">
<div class="viz-container">
<!-- Pixel Space -->
<div class="space-block">
<div class="space-header">
<el-icon :size="20"><Picture /></el-icon>
<span class="space-title">像素空间 (Pixel Space)</span>
</div>
<div class="grid-wrapper pixel-wrapper">
<div class="pixel-grid">
<div
v-for="n in 256"
:key="n"
class="pixel-cell"
:style="getPixelStyle(n)"
></div>
</div>
<div class="grid-overlay">
<span>HD Image</span>
<span class="res-tag">1024x1024</span>
</div>
</div>
<div class="data-stats">
<div class="stat-row">
<span class="label">维度:</span>
<span class="value">3 (RGB)</span>
</div>
<div class="stat-row">
<span class="label">数据量:</span>
<span class="value">~300</span>
</div>
</div>
</div>
<!-- Transformation -->
<div class="transform-process">
<div class="process-arrow">
<div class="vae-box">
<span class="vae-label">VAE Encoder</span>
<el-icon><Filter /></el-icon>
</div>
<el-icon :size="24" class="arrow-icon"><Right /></el-icon>
</div>
<el-tag type="danger" size="small" effect="dark" class="compress-tag"
>压缩 48x</el-tag
>
</div>
<!-- Latent Space -->
<div class="space-block highlight">
<div class="space-header">
<el-icon :size="20" color="#E6A23C"><Cpu /></el-icon>
<span class="space-title">潜空间 (Latent Space)</span>
</div>
<div class="grid-wrapper latent-wrapper">
<div class="latent-grid">
<div
v-for="n in 16"
:key="n"
class="latent-cell"
:style="getLatentStyle(n)"
></div>
</div>
<div class="grid-overlay">
<span>Latent Feature</span>
<span class="res-tag">64x64</span>
</div>
</div>
<div class="data-stats">
<div class="stat-row">
<span class="label">维度:</span>
<span class="value">4 (Channels)</span>
</div>
<div class="stat-row">
<span class="label">数据量:</span>
<span class="value">~1.6</span>
</div>
</div>
<div class="viz-card">
<!-- Left: The Output (Pixel Space) -->
<div class="preview-section">
<div class="emoji-display" :style="{ transform: `scale(${1 + zoomLevel})` }">
{{ currentEmoji }}
</div>
<div class="label">像素空间 (Pixel Space)</div>
<div class="sub-label">最终看到的图像</div>
</div>
<el-divider />
<!-- Center: The Mechanism -->
<div class="connection">
<div class="arrow"> 映射 </div>
<div class="vae-tag">VAE Decoder</div>
</div>
<el-row :gutter="20">
<el-col :span="8">
<el-statistic title="压缩比" value="1:48">
<template #suffix>
<el-icon style="vertical-align: -0.125em"><Scissor /></el-icon>
</template>
</el-statistic>
</el-col>
<el-col :span="16">
<el-alert
title="为什么要压缩?"
type="success"
:closable="false"
description="直接处理 300 万个像素太慢了。VAE 把图像压缩成「压缩饼干」(潜变量),保留了核心特征(语义、构图),扔掉了冗余细节。AI 在这个小空间里画画,速度飞快!"
show-icon
/>
</el-col>
</el-row>
</el-card>
<!-- Right: The Input (Latent Space) -->
<div class="control-section">
<div class="latent-grid" ref="gridRef" @mousedown="startDrag" @touchstart="startDrag">
<div class="grid-lines"></div>
<div class="axis-label x-axis">开心值 (Happiness)</div>
<div class="axis-label y-axis">惊讶值 (Surprise)</div>
<!-- The Latent Point -->
<div
class="latent-point"
:style="{ left: `${point.x}%`, top: `${point.y}%` }"
>
<div class="tooltip">Latent Vector: [{{ ((point.x-50)/50).toFixed(1) }}, {{ ((50-point.y)/50).toFixed(1) }}]</div>
</div>
</div>
<div class="label">潜空间 (Latent Space)</div>
<div class="sub-label">拖动红点改变特征</div>
</div>
</div>
<div class="info-bar">
<span class="icon">💡</span>
<span>
<strong>核心原理</strong>
在像素空间里修改图片很难要改几千个像素但在潜空间里我们只需要修改两个坐标开心值惊讶值就能生成完全不同的表情这就是 AI "画画" 的本质在数学空间里找坐标
</span>
</div>
</div>
</template>
<script setup>
import { Picture, Cpu, Right, Filter, Scissor } from '@element-plus/icons-vue'
import { ref, computed, onMounted, onUnmounted } from 'vue'
const getPixelStyle = (n) => {
// Simulate a natural image with smooth color transitions
const r = 100 + Math.sin(n * 0.1) * 50
const g = 150 + Math.cos(n * 0.1) * 50
const b = 200 + Math.sin(n * 0.05) * 50
return {
backgroundColor: `rgb(${r}, ${g}, ${b})`,
opacity: 0.8 + Math.random() * 0.2
const gridRef = ref(null)
const isDragging = ref(false)
const point = ref({ x: 50, y: 50 }) // Percentage 0-100
const zoomLevel = ref(0)
// Emoji map based on quadrants
// X: Unhappy -> Happy
// Y: Calm -> Surprised (Top is 0 in CSS, so small Y is high surprise?)
// Let's map:
// X (0-100): Sad -> Happy
// Y (0-100): Surprised -> Sleepy (Top is 0, so 0 is Surprised, 100 is Sleepy)
const currentEmoji = computed(() => {
const x = point.value.x // 0 (Sad) to 100 (Happy)
const y = point.value.y // 0 (Surprised) to 100 (Sleepy)
if (x < 33) { // Sad Zone
if (y < 33) return '😨' // Sad + Surprised = Fear
if (y > 66) return '😪' // Sad + Sleepy = Tired
return '😢' // Just Sad
} else if (x > 66) { // Happy Zone
if (y < 33) return '🤩' // Happy + Surprised = Starstruck
if (y > 66) return '😌' // Happy + Sleepy = Relieved
return '😃' // Just Happy
} else { // Neutral Zone
if (y < 33) return '😮' // Neutral + Surprised
if (y > 66) return '😴' // Neutral + Sleepy
return '😐' // Just Neutral
}
})
const handleMove = (event) => {
if (!isDragging.value) return
const grid = gridRef.value.getBoundingClientRect()
const clientX = event.touches ? event.touches[0].clientX : event.clientX
const clientY = event.touches ? event.touches[0].clientY : event.clientY
let newX = ((clientX - grid.left) / grid.width) * 100
let newY = ((clientY - grid.top) / grid.height) * 100
// Clamp
newX = Math.max(0, Math.min(100, newX))
newY = Math.max(0, Math.min(100, newY))
point.value = { x: newX, y: newY }
}
const getLatentStyle = (n) => {
// Simulate high-level features (more abstract, high contrast colors)
const hue = (n * 137) % 360
return {
backgroundColor: `hsl(${hue}, 70%, 60%)`,
boxShadow: `0 0 5px hsl(${hue}, 70%, 60%)`
}
const startDrag = (event) => {
isDragging.value = true
handleMove(event)
// Prevent default to stop scrolling on mobile
if (event.type === 'touchstart') event.preventDefault()
}
const stopDrag = () => {
isDragging.value = false
}
onMounted(() => {
window.addEventListener('mousemove', handleMove)
window.addEventListener('mouseup', stopDrag)
window.addEventListener('touchmove', handleMove)
window.addEventListener('touchend', stopDrag)
})
onUnmounted(() => {
window.removeEventListener('mousemove', handleMove)
window.removeEventListener('mouseup', stopDrag)
window.removeEventListener('touchmove', handleMove)
window.removeEventListener('touchend', stopDrag)
})
</script>
<style scoped>
.latent-space-viz {
margin: 20px 0;
font-family: var(--vp-font-family-base);
}
.viz-container {
.viz-card {
display: flex;
align-items: center;
justify-content: center;
justify-content: space-between;
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
padding: 24px;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.space-block {
.preview-section, .control-section {
flex: 1;
min-width: 200px;
background: var(--el-fill-color-lighter);
border-radius: 8px;
padding: 15px;
border: 1px solid var(--el-border-color-lighter);
}
.space-block.highlight {
border-color: var(--el-color-warning-light-5);
background: var(--el-color-warning-light-9);
}
.space-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 15px;
font-weight: bold;
color: var(--el-text-color-primary);
}
.grid-wrapper {
position: relative;
background: #000;
border-radius: 4px;
overflow: hidden;
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
}
.pixel-wrapper {
height: 160px;
}
.latent-wrapper {
height: 100px; /* Smaller representation */
width: 100px;
margin: 0 auto 15px auto;
}
.pixel-grid {
display: grid;
grid-template-columns: repeat(16, 1fr);
width: 100%;
height: 100%;
}
.pixel-cell {
width: 100%;
height: 100%;
}
.latent-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
width: 100%;
height: 100%;
gap: 2px;
padding: 5px;
}
.latent-cell {
width: 100%;
height: 100%;
border-radius: 2px;
}
.grid-overlay {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background: rgba(0, 0, 0, 0.6);
color: #fff;
font-size: 0.75em;
padding: 4px 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.res-tag {
font-family: monospace;
opacity: 0.8;
}
.data-stats {
font-size: 0.85em;
color: var(--el-text-color-secondary);
}
.stat-row {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
}
.transform-process {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
color: var(--el-text-color-secondary);
min-width: 200px;
}
.process-arrow {
.emoji-display {
font-size: 80px;
line-height: 1;
height: 100px;
display: flex;
align-items: center;
gap: 5px;
justify-content: center;
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: default;
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.1));
}
.vae-box {
border: 1px solid var(--el-border-color);
border-radius: 4px;
.latent-grid {
width: 200px;
height: 200px;
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-brand);
border-radius: 12px;
position: relative;
cursor: crosshair;
overflow: hidden;
box-shadow: inset 0 2px 8px rgba(0,0,0,0.05);
}
.grid-lines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(var(--vp-c-divider) 1px, transparent 1px),
linear-gradient(90deg, var(--vp-c-divider) 1px, transparent 1px);
background-size: 20px 20px;
opacity: 0.3;
}
.latent-point {
width: 20px;
height: 20px;
background: var(--vp-c-brand);
border: 3px solid #fff;
border-radius: 50%;
position: absolute;
transform: translate(-50%, -50%);
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
transition: transform 0.1s;
}
.latent-point:hover {
transform: translate(-50%, -50%) scale(1.2);
}
.tooltip {
position: absolute;
bottom: 25px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.8);
color: #fff;
padding: 4px 8px;
font-size: 0.8em;
display: flex;
align-items: center;
gap: 4px;
background: #fff;
border-radius: 4px;
font-size: 10px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
}
.compress-tag {
transform: scale(0.9);
.latent-point:hover .tooltip,
.latent-point:active .tooltip {
opacity: 1;
}
.axis-label {
position: absolute;
font-size: 10px;
color: var(--vp-c-text-2);
pointer-events: none;
}
.x-axis {
bottom: 4px;
right: 8px;
}
.y-axis {
top: 8px;
left: 8px;
writing-mode: vertical-rl;
transform: rotate(180deg);
}
.connection {
display: flex;
flex-direction: column;
align-items: center;
color: var(--vp-c-text-2);
font-size: 12px;
}
.arrow {
margin-bottom: 4px;
font-weight: bold;
}
.vae-tag {
background: var(--vp-c-brand-dimm);
color: var(--vp-c-brand-dark);
padding: 2px 8px;
border-radius: 4px;
font-size: 10px;
font-weight: 600;
}
.label {
margin-top: 12px;
font-weight: 600;
color: var(--vp-c-text-1);
}
.sub-label {
font-size: 12px;
color: var(--vp-c-text-2);
}
.info-bar {
margin-top: 16px;
background: var(--vp-c-bg-alt);
padding: 12px 16px;
border-radius: 8px;
font-size: 13px;
line-height: 1.5;
color: var(--vp-c-text-2);
display: flex;
gap: 8px;
}
.icon {
font-size: 16px;
}
@media (max-width: 600px) {
.viz-container {
flex-direction: column;
.viz-card {
flex-direction: column-reverse;
}
.transform-process {
.connection {
transform: rotate(90deg);
margin: 10px 0;
}
.compress-tag {
display: none; /* Hide tag when rotated to avoid layout issues */
}
}
</style>