feat: update docs and components, fix DLQ demo bug

This commit is contained in:
sanbuphy
2026-01-18 12:21:49 +08:00
parent 26ed39e1eb
commit e41063a1cd
159 changed files with 54236 additions and 2525 deletions
@@ -103,9 +103,27 @@ const closeTab = (index) => {
const activeSidebarView = ref('EXPLORER') // 'EXPLORER' | 'EXTENSIONS'
const extensions = ref([
{ id: 'python', name: 'Python', description: 'IntelliSense, linting, debugging...', installed: false, installing: false },
{ id: 'cpp', name: 'C/C++', description: 'C/C++ IntelliSense, debugging...', installed: false, installing: false },
{ id: 'vue', name: 'Vue - Official', description: 'Language support for Vue 3', installed: false, installing: false },
{
id: 'python',
name: 'Python',
description: 'IntelliSense, linting, debugging...',
installed: false,
installing: false
},
{
id: 'cpp',
name: 'C/C++',
description: 'C/C++ IntelliSense, debugging...',
installed: false,
installing: false
},
{
id: 'vue',
name: 'Vue - Official',
description: 'Language support for Vue 3',
installed: false,
installing: false
}
])
const searchQuery = ref('')
@@ -113,22 +131,23 @@ const searchQuery = ref('')
const typeText = async (text, setter) => {
for (let i = 0; i < text.length; i++) {
setter(text.substring(0, i + 1))
await new Promise(r => setTimeout(r, 100)) // typing speed
await new Promise((r) => setTimeout(r, 100)) // typing speed
}
}
const filteredExtensions = computed(() => {
if (!searchQuery.value) return extensions.value
const query = searchQuery.value.toLowerCase()
return extensions.value.filter(ext =>
ext.name.toLowerCase().includes(query) ||
ext.description.toLowerCase().includes(query)
return extensions.value.filter(
(ext) =>
ext.name.toLowerCase().includes(query) ||
ext.description.toLowerCase().includes(query)
)
})
const installExtension = (id) => {
const ext = extensions.value.find(e => e.id === id)
if(ext && !ext.installed && !ext.installing) {
const ext = extensions.value.find((e) => e.id === id)
if (ext && !ext.installed && !ext.installing) {
ext.installing = true
setTimeout(() => {
ext.installing = false
@@ -302,64 +321,67 @@ const vClickOutside = {
}
const startTour = async () => {
if (isAutoPlaying.value) return
isAutoPlaying.value = true
cursorVisible.value = true
if (isAutoPlaying.value) return
isAutoPlaying.value = true
cursorVisible.value = true
// Reset UI state to ensure all elements are visible
activeFileIndex.value = 0
activePanel.value = 'TERMINAL'
sidebarVisible.value = false
panelVisible.value = true
activeMenu.value = null
hoverInfo.value = ''
searchQuery.value = '' // Reset search
activeSidebarView.value = 'EXPLORER' // Reset sidebar view
// Reset UI state to ensure all elements are visible
activeFileIndex.value = 0
activePanel.value = 'TERMINAL'
sidebarVisible.value = false
panelVisible.value = true
activeMenu.value = null
hoverInfo.value = ''
searchQuery.value = '' // Reset search
activeSidebarView.value = 'EXPLORER' // Reset sidebar view
const container = vscodeMockRef.value
if (!container) return
const container = vscodeMockRef.value
if (!container) return
const moveCursorTo = (selector, infoText, action = null) => {
return new Promise((resolve) => {
if (action) action()
const moveCursorTo = (selector, infoText, action = null) => {
return new Promise((resolve) => {
if (action) action()
// Small delay to allow UI updates (like opening menus)
setTimeout(() => {
const el = container.querySelector(selector)
if (el) {
// Recalculate container rect in case of scroll
const containerRect = container.getBoundingClientRect()
const rect = el.getBoundingClientRect()
// Calculate relative position
cursorX.value = rect.left - containerRect.left + rect.width / 2
cursorY.value = rect.top - containerRect.top + rect.height / 2
// Small delay to allow UI updates (like opening menus)
setTimeout(() => {
const el = container.querySelector(selector)
if (el) {
// Recalculate container rect in case of scroll
const containerRect = container.getBoundingClientRect()
const rect = el.getBoundingClientRect()
// Update highlight box
// Use box-sizing: border-box in CSS
// Match exact size (border will be drawn inside the element area)
highlightStyle.value = {
top: rect.top - containerRect.top + 'px',
left: rect.left - containerRect.left + 'px',
width: rect.width + 'px',
height: rect.height + 'px'
}
highlightVisible.value = true
// Calculate relative position
cursorX.value = rect.left - containerRect.left + rect.width / 2
cursorY.value = rect.top - containerRect.top + rect.height / 2
hoverInfo.value = infoText
} else {
// Fallback if element not found: just proceed after delay
// This prevents the tour from hanging forever
console.warn(`Tour element not found: ${selector}`)
// Update highlight box
// Use box-sizing: border-box in CSS
// Match exact size (border will be drawn inside the element area)
highlightStyle.value = {
top: rect.top - containerRect.top + 'px',
left: rect.left - containerRect.left + 'px',
width: rect.width + 'px',
height: rect.height + 'px'
}
tourTimeout = setTimeout(() => {
highlightVisible.value = true
hoverInfo.value = infoText
} else {
// Fallback if element not found: just proceed after delay
// This prevents the tour from hanging forever
console.warn(`Tour element not found: ${selector}`)
}
tourTimeout = setTimeout(
() => {
highlightVisible.value = false
resolve()
}, el ? 2500 : 500) // Shorter delay if skipped
}, 800) // Increased delay for better stability
})
}
},
el ? 2500 : 500
) // Shorter delay if skipped
}, 800) // Increased delay for better stability
})
}
// --- Tour Segments ---
@@ -369,25 +391,17 @@ const startTour = async () => {
if (!isAutoPlaying.value) return
// Menus
await moveCursorTo(
'.menu-bar-container',
'菜单栏:所有功能'
)
await moveCursorTo('.menu-bar-container', '菜单栏:所有功能')
if (!isAutoPlaying.value) return
// Demonstrate clicking a menu
await moveCursorTo(
'.menu-item:nth-child(1)',
'文件菜单:文件操作',
() => toggleMenu('File')
await moveCursorTo('.menu-item:nth-child(1)', '文件菜单:文件操作', () =>
toggleMenu('File')
)
if (!isAutoPlaying.value) return
// Show a specific item in the dropdown
await moveCursorTo(
'.dropdown-item:nth-child(1)',
'新建文件:创建空文件'
)
await moveCursorTo('.dropdown-item:nth-child(1)', '新建文件:创建空文件')
if (!isAutoPlaying.value) return
// Close menu
@@ -397,55 +411,36 @@ const startTour = async () => {
await moveCursorTo('.nav-arrows', '导航按钮:后退/前进')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.search-box',
'命令中心:快速搜索'
)
await moveCursorTo('.search-box', '命令中心:快速搜索')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.layout-controls',
'布局控制:切换视图'
)
await moveCursorTo('.layout-controls', '布局控制:切换视图')
}
const runActivityBarTour = async () => {
// --- 2. Activity Bar (Left) ---
await moveCursorTo(
'.activity-bar',
'活动栏:切换视图'
)
await moveCursorTo('.activity-bar', '活动栏:切换视图')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.icon[title="Explorer"]',
'资源管理器:管理文件',
() => { sidebarVisible.value = true }
() => {
sidebarVisible.value = true
}
)
if (!isAutoPlaying.value) return
await moveCursorTo(
'.icon[title="Search"]',
'全局搜索:查找替换'
)
await moveCursorTo('.icon[title="Search"]', '全局搜索:查找替换')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.icon[title="Source Control"]',
'源代码管理:Git'
)
await moveCursorTo('.icon[title="Source Control"]', '源代码管理:Git')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.icon[title="Run and Debug"]',
'运行和调试:调试代码'
)
await moveCursorTo('.icon[title="Run and Debug"]', '运行和调试:调试代码')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.icon[title="Extensions"]',
'扩展商店:安装插件'
)
await moveCursorTo('.icon[title="Extensions"]', '扩展商店:安装插件')
if (!isAutoPlaying.value) return
await moveCursorTo('.icon[title="Accounts"]', '账户:同步设置')
@@ -460,7 +455,7 @@ const startTour = async () => {
sidebarVisible.value = true
await new Promise((r) => setTimeout(r, 300))
}
await moveCursorTo('.sidebar', '侧边栏:详细内容')
if (!isAutoPlaying.value) return
@@ -470,10 +465,7 @@ const startTour = async () => {
)
if (!isAutoPlaying.value) return
await moveCursorTo(
'.sidebar-section:nth-child(3)',
'项目文件树:项目结构'
)
await moveCursorTo('.sidebar-section:nth-child(3)', '项目文件树:项目结构')
}
const runEditorTour = async () => {
@@ -487,22 +479,13 @@ const startTour = async () => {
}
// --- 4. Editor Area ---
await moveCursorTo(
'.tabs',
'标签页:已打开文件'
)
await moveCursorTo('.tabs', '标签页:已打开文件')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.breadcrumbs',
'路径导航:文件路径'
)
await moveCursorTo('.breadcrumbs', '路径导航:文件路径')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.code-wrapper',
'编辑区:编写代码'
)
await moveCursorTo('.code-wrapper', '编辑区:编写代码')
if (!isAutoPlaying.value) return
await moveCursorTo('.minimap', '缩略图:预览代码')
@@ -513,33 +496,21 @@ const startTour = async () => {
await moveCursorTo('.bottom-panel', '底部面板:集成工具')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.panel-tabs',
'面板切换:切换工具'
)
await moveCursorTo('.panel-tabs', '面板切换:切换工具')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.terminal-content',
'终端:运行命令'
)
await moveCursorTo('.terminal-content', '终端:运行命令')
}
const runStatusTour = async () => {
// --- 6. Status Bar ---
await moveCursorTo(
'.status-bar',
'状态栏:全局信息'
)
await moveCursorTo('.status-bar', '状态栏:全局信息')
if (!isAutoPlaying.value) return
await moveCursorTo('.status-left', '左侧信息:Git/错误')
if (!isAutoPlaying.value) return
await moveCursorTo(
'.status-right',
'右侧信息:环境信息'
)
await moveCursorTo('.status-right', '右侧信息:环境信息')
}
try {
@@ -553,8 +524,8 @@ const startTour = async () => {
}
if (mode === 'all' || mode === 'extensions') {
// --- Extensions Tour ---
await moveCursorTo(
// --- Extensions Tour ---
await moveCursorTo(
'.icon[title="Extensions"]',
'扩展商店:安装插件',
() => toggleSidebarView('EXTENSIONS')
@@ -565,7 +536,7 @@ const startTour = async () => {
'.sidebar-search input',
'搜索插件:输入 python',
async () => {
await typeText('python', (v) => searchQuery.value = v)
await typeText('python', (v) => (searchQuery.value = v))
}
)
if (!isAutoPlaying.value) return
@@ -576,17 +547,13 @@ const startTour = async () => {
() => installExtension('python')
)
if (!isAutoPlaying.value) return
// Switch back to explorer for next steps if in 'all' mode
if (mode === 'all') {
await moveCursorTo(
'.icon[title="Explorer"]',
'返回资源管理器',
() => {
toggleSidebarView('EXPLORER')
searchQuery.value = '' // Clear search when leaving
}
)
await moveCursorTo('.icon[title="Explorer"]', '返回资源管理器', () => {
toggleSidebarView('EXPLORER')
searchQuery.value = '' // Clear search when leaving
})
}
}
@@ -725,9 +692,7 @@ onUnmounted(() => {
</div>
<div
class="menu-bar-container"
@mouseenter.stop="
showInfo('菜单栏:功能入口')
"
@mouseenter.stop="showInfo('菜单栏:功能入口')"
@mouseleave="clearInfo"
>
<div
@@ -832,7 +797,9 @@ onUnmounted(() => {
<div class="top-icons">
<div
class="icon"
:class="{ active: activeSidebarView === 'EXPLORER' && sidebarVisible }"
:class="{
active: activeSidebarView === 'EXPLORER' && sidebarVisible
}"
title="Explorer"
@click="toggleSidebarView('EXPLORER')"
@mouseenter.stop="showInfo('资源管理器:文件管理')"
@@ -985,7 +952,9 @@ onUnmounted(() => {
</div>
<div
class="icon"
:class="{ active: activeSidebarView === 'EXTENSIONS' && sidebarVisible }"
:class="{
active: activeSidebarView === 'EXTENSIONS' && sidebarVisible
}"
title="Extensions"
@click="toggleSidebarView('EXTENSIONS')"
@mouseenter.stop="showInfo('扩展:插件')"
@@ -1105,9 +1074,7 @@ onUnmounted(() => {
<div
class="sidebar"
v-show="sidebarVisible"
@mouseenter.stop="
showInfo('侧边栏:详细内容')
"
@mouseenter.stop="showInfo('侧边栏:详细内容')"
@mouseleave="clearInfo"
>
<div v-if="activeSidebarView === 'EXPLORER'" class="sidebar-content">
@@ -1169,33 +1136,56 @@ onUnmounted(() => {
</div>
</div>
</div>
<div v-else-if="activeSidebarView === 'EXTENSIONS'" class="sidebar-content">
<div class="sidebar-header">
<div
v-else-if="activeSidebarView === 'EXTENSIONS'"
class="sidebar-content"
>
<div class="sidebar-header">
<span>EXTENSIONS</span>
<span class="sidebar-dots"></span>
</div>
<div class="sidebar-search">
<input type="text" placeholder="Search Extensions in Marketplace" :value="searchQuery" readonly />
<input
type="text"
placeholder="Search Extensions in Marketplace"
:value="searchQuery"
readonly
/>
</div>
<div class="sidebar-section expanded">
<div class="section-header"> POPULAR</div>
<div class="extension-list">
<div v-for="ext in filteredExtensions" :key="ext.id" class="extension-item">
<div
v-for="ext in filteredExtensions"
:key="ext.id"
class="extension-item"
>
<div class="extension-icon"></div>
<div class="extension-info">
<div class="extension-name">
{{ ext.name }}
<span v-if="ext.installed" class="installed-badge"></span>
<span v-if="ext.installed" class="installed-badge"
></span
>
</div>
<div class="extension-desc">{{ ext.description }}</div>
<div class="extension-actions">
<button
class="install-btn"
:class="{ installing: ext.installing, installed: ext.installed }"
<button
class="install-btn"
:class="{
installing: ext.installing,
installed: ext.installed
}"
@click.stop="installExtension(ext.id)"
>
{{ ext.installed ? 'Manage' : (ext.installing ? 'Installing' : 'Install') }}
{{
ext.installed
? 'Manage'
: ext.installing
? 'Installing'
: 'Install'
}}
</button>
</div>
</div>
@@ -1436,9 +1426,7 @@ onUnmounted(() => {
<!-- Status Bar -->
<div
class="status-bar"
@mouseenter.stop="
showInfo('状态栏:环境信息')
"
@mouseenter.stop="showInfo('状态栏:环境信息')"
@mouseleave="clearInfo"
>
<div class="status-left">
@@ -2416,8 +2404,17 @@ onUnmounted(() => {
}
@keyframes highlightPulse {
0% { transform: scale(1); opacity: 0.7; }
50% { transform: scale(1.02); opacity: 0.9; }
100% { transform: scale(1); opacity: 0.7; }
0% {
transform: scale(1);
opacity: 0.7;
}
50% {
transform: scale(1.02);
opacity: 0.9;
}
100% {
transform: scale(1);
opacity: 0.7;
}
}
</style>