Files
test-repo/docs/.vitepress/theme/components/appendix/ide-intro/IdeArchitectureDemo.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

525 lines
13 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref, computed } from 'vue'
const currentScenario = ref('editor') // 'editor' | 'extension' | 'full'
const isRunning = ref(false)
const logs = ref([])
const activeStep = ref('') // 'start' | 'error-editor' | 'extension' | 'error-env' | 'env' | 'result'
const scenarios = {
editor: {
tab: '1. 仅编辑器',
title: '场景 1: 只有 VS Code (纯文本模式)',
desc: '就像用 Windows 记事本写代码。虽然能打字,但它根本不懂什么是 Python。',
result: '❌ 失败:VS Code 把代码当成普通文本,不知道该怎么运行。'
},
extension: {
tab: '2. +插件',
title: '场景 2: 安装了插件 (缺环境)',
desc: '你安装了 Python 插件。插件知道“运行”意味着要找 Python 程序,但你的电脑里并没有安装 Python。',
result: '⚠️ 报错:插件生成了指令,但在系统里找不到 "python.exe"。'
},
full: {
tab: '3. +环境 (完整)',
title: '场景 3: 完整形态 (IDE + 插件 + 环境)',
desc: '你安装了 Python 解释器。插件生成指令,解释器接收并执行,完美配合。',
result: '✅ 成功:Hello World'
}
}
const run = async () => {
if (isRunning.value) return
isRunning.value = true
logs.value = []
activeStep.value = 'start'
await wait(600)
if (currentScenario.value === 'editor') {
logs.value.push('VS Code: "这是什么文件?我不认识。"')
logs.value.push('VS Code: "我只是个打字机,无法运行。"')
activeStep.value = 'error-editor'
} else {
// Has extension
activeStep.value = 'extension'
await wait(800)
if (currentScenario.value === 'extension') {
logs.value.push('> python main.py')
await wait(600)
logs.value.push("Error: command 'python' not found")
logs.value.push("系统: 找不到 Python 解释器")
activeStep.value = 'error-env'
} else {
// Full
logs.value.push('> python main.py')
activeStep.value = 'env'
await wait(1200)
activeStep.value = 'result'
logs.value.push('Hello World')
}
}
isRunning.value = false
}
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms))
const setScenario = (key) => {
if (isRunning.value) return
currentScenario.value = key
logs.value = []
activeStep.value = ''
}
</script>
<template>
<div class="arch-demo">
<div class="demo-header">
<div class="title">🛠 IDE 核心机制模拟器</div>
<div class="subtitle">点击下方标签体验不同配置下的运行结果理解为什么缺一不可</div>
</div>
<!-- Tab Selection -->
<div class="tabs">
<div
v-for="(conf, key) in scenarios"
:key="key"
class="tab"
:class="{ active: currentScenario === key }"
@click="setScenario(key)"
>
{{ conf.tab }}
</div>
</div>
<div class="scenario-desc">
<strong>{{ scenarios[currentScenario].title }}</strong>
<p>{{ scenarios[currentScenario].desc }}</p>
</div>
<div class="diagram-container">
<!-- Layer 1: VS Code -->
<div class="component vscode" :class="{ dim: activeStep === 'env' }">
<div class="comp-label">1. 外壳 (VS Code)</div>
<div class="editor-window">
<div class="file-tab">main.py</div>
<div class="code-area">
<span style="color: #c586c0">print</span>(<span style="color: #ce9178">"Hello"</span>)
</div>
<button class="run-btn-small" @click="run" :disabled="isRunning" title="点击运行">
{{ isRunning ? '...' : '▶ 运行' }}
</button>
</div>
<div class="status-badge error" v-if="activeStep === 'error-editor'">
🚫 不懂怎么运行
</div>
</div>
<!-- Connector 1 -->
<div class="connector">
<div class="line" :class="{ active: ['extension', 'env', 'result', 'error-env'].includes(activeStep) }"></div>
<div class="arrow-tip" :class="{ active: ['extension', 'env', 'result', 'error-env'].includes(activeStep) }"></div>
</div>
<!-- Layer 2: Extension -->
<div class="component extension" :class="{ missing: currentScenario === 'editor', active: activeStep === 'extension' }">
<div class="comp-label">2. 中介 (插件)</div>
<div class="comp-box">
<div v-if="currentScenario === 'editor'" class="missing-content">
<span class="icon"></span> 未安装插件
</div>
<div v-else class="active-content">
<div class="icon">🧩</div>
<div class="text">Python 插件</div>
<div class="action" v-if="activeStep === 'extension' || activeStep === 'env' || activeStep === 'error-env'">
生成指令: <code>python main.py</code>
</div>
</div>
</div>
</div>
<!-- Connector 2 -->
<div class="connector">
<div class="line" :class="{ active: ['env', 'result'].includes(activeStep) }"></div>
<div class="arrow-tip" :class="{ active: ['env', 'result'].includes(activeStep) }"></div>
</div>
<!-- Layer 3: Environment -->
<div class="component env" :class="{ missing: currentScenario !== 'full', active: activeStep === 'env' }">
<div class="comp-label">3. 引擎 (环境)</div>
<div class="comp-box">
<div v-if="currentScenario !== 'full'" class="missing-content">
<span class="icon"></span> 未安装环境
</div>
<div v-else class="active-content">
<div class="icon"></div>
<div class="text">Python 解释器</div>
<div class="action" v-if="activeStep === 'env'">
<span class="spin"></span> 正在计算...
</div>
<div class="action success" v-if="activeStep === 'result'">
计算完成
</div>
</div>
</div>
<div class="status-badge error" v-if="activeStep === 'error-env'">
🚫 找不到程序
</div>
</div>
</div>
<!-- Output Console -->
<div class="terminal-box">
<div class="term-header">
<span class="term-icon">_</span> 终端 (Terminal)
</div>
<div class="term-body">
<div v-for="(l, i) in logs" :key="i" class="log-line" :class="{ error: l.includes('Error') || l.includes('失败') }">
{{ l }}
</div>
<div v-if="logs.length === 0" class="placeholder">点击上方运行按钮开始...</div>
</div>
</div>
<div class="result-bar" :class="{ success: scenarios[currentScenario].result.includes('成功'), error: !scenarios[currentScenario].result.includes('成功') }" v-if="!isRunning && logs.length > 0">
{{ scenarios[currentScenario].result }}
</div>
</div>
</template>
<style scoped>
.arch-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
padding: 24px;
background: var(--vp-c-bg-soft);
margin: 24px 0;
font-family: var(--vp-font-family-base);
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.demo-header {
text-align: center;
margin-bottom: 20px;
}
.title {
font-size: 18px;
font-weight: bold;
color: var(--vp-c-text-1);
}
.subtitle {
font-size: 13px;
color: var(--vp-c-text-2);
margin-top: 4px;
}
/* Tabs */
.tabs {
display: flex;
background: var(--vp-c-bg-mute);
border-radius: 8px;
padding: 4px;
margin-bottom: 16px;
gap: 4px;
}
.tab {
flex: 1;
text-align: center;
padding: 8px;
font-size: 14px;
cursor: pointer;
border-radius: 6px;
color: var(--vp-c-text-2);
transition: all 0.2s;
font-weight: 500;
}
.tab:hover {
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
}
.tab.active {
background: var(--vp-c-bg);
color: var(--vp-c-brand);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
font-weight: bold;
}
.scenario-desc {
background: var(--vp-c-bg-alt);
border-left: 4px solid var(--vp-c-brand);
padding: 12px 16px;
border-radius: 4px;
margin-bottom: 24px;
font-size: 14px;
line-height: 1.5;
}
.scenario-desc strong {
display: block;
margin-bottom: 4px;
color: var(--vp-c-text-1);
}
.scenario-desc p {
margin: 0;
color: var(--vp-c-text-2);
}
/* Diagram */
.diagram-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
margin-bottom: 24px;
}
.component {
width: 100%;
max-width: 320px;
position: relative;
transition: all 0.3s;
}
.comp-label {
font-size: 12px;
font-weight: bold;
color: var(--vp-c-text-3);
margin-bottom: 6px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* VS Code Style */
.vscode .editor-window {
background: #1e1e1e;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
border: 1px solid #333;
}
.vscode .file-tab {
background: #2d2d2d;
color: #fff;
padding: 4px 12px;
font-size: 12px;
border-bottom: 1px solid #1e1e1e;
width: fit-content;
}
.vscode .code-area {
padding: 12px;
font-family: 'Consolas', monospace;
font-size: 14px;
color: #d4d4d4;
display: flex;
align-items: center;
justify-content: space-between;
}
.run-btn-small {
background: #007acc;
color: white;
border: none;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: background 0.2s;
}
.run-btn-small:hover {
background: #0062a3;
}
.run-btn-small:disabled {
background: #444;
cursor: not-allowed;
}
/* Extension & Env Box Style */
.comp-box {
background: var(--vp-c-bg);
border: 2px solid var(--vp-c-divider);
border-radius: 8px;
padding: 12px;
min-height: 60px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.component.missing .comp-box {
border-style: dashed;
background: var(--vp-c-bg-alt);
opacity: 0.7;
}
.component.active .comp-box {
border-color: var(--vp-c-brand);
box-shadow: 0 0 0 2px rgba(var(--vp-c-brand-rgb), 0.2);
}
.missing-content {
color: var(--vp-c-text-3);
font-size: 13px;
display: flex;
align-items: center;
gap: 6px;
}
.active-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
width: 100%;
}
.active-content .icon {
font-size: 20px;
}
.active-content .text {
font-weight: 600;
font-size: 14px;
}
.active-content .action {
font-size: 12px;
background: var(--vp-c-bg-mute);
padding: 2px 8px;
border-radius: 4px;
margin-top: 4px;
font-family: monospace;
animation: fadeIn 0.3s;
}
.active-content .action.success {
color: var(--vp-c-green);
background: var(--vp-c-green-dimm);
}
/* Connectors */
.connector {
height: 24px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.line {
width: 2px;
height: 100%;
background: var(--vp-c-divider);
transition: background 0.3s;
}
.line.active {
background: var(--vp-c-brand);
}
.arrow-tip {
position: absolute;
bottom: -4px;
font-size: 12px;
color: var(--vp-c-divider);
transition: color 0.3s;
background: var(--vp-c-bg-soft);
}
.arrow-tip.active {
color: var(--vp-c-brand);
}
/* Status Badges */
.status-badge {
position: absolute;
top: 50%;
right: -100px;
transform: translateY(-50%);
padding: 6px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
white-space: nowrap;
animation: slideIn 0.3s;
}
.status-badge.error {
background: #ffe6e6;
color: #d93025;
border: 1px solid #ffcdd2;
}
/* Terminal */
.terminal-box {
background: #1e1e1e;
border-radius: 8px;
overflow: hidden;
font-family: 'Consolas', monospace;
border: 1px solid #333;
}
.term-header {
background: #2d2d2d;
color: #ccc;
padding: 4px 12px;
font-size: 12px;
border-bottom: 1px solid #333;
}
.term-body {
padding: 12px;
min-height: 80px;
font-size: 13px;
color: #fff;
}
.log-line {
margin-bottom: 4px;
}
.log-line.error {
color: #ff6b68;
}
.placeholder {
color: #666;
font-style: italic;
}
.result-bar {
margin-top: 16px;
padding: 10px;
border-radius: 6px;
text-align: center;
font-weight: bold;
font-size: 14px;
}
.result-bar.success {
background: var(--vp-c-green-dimm);
color: var(--vp-c-green-dark);
}
.result-bar.error {
background: var(--vp-c-red-dimm);
color: var(--vp-c-red-dark);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideIn {
from { opacity: 0; transform: translate(-10px, -50%); }
to { opacity: 1; transform: translate(0, -50%); }
}
.spin {
display: inline-block;
animation: spin 2s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Mobile Responsive */
@media (max-width: 600px) {
.status-badge {
position: relative;
right: auto;
top: auto;
transform: none;
margin-top: 8px;
display: inline-block;
width: 100%;
text-align: center;
}
}
</style>