feat(docs): enhance JavaScript runtime and browser-as-os content
refactor(demos): improve variable box, scope, and type annotation demos style(demos): update visual styles and animations for better UX docs(browser-as-os): restructure content with tables and practical examples feat(demos): add new TypeScript and runtime environment demos
This commit is contained in:
@@ -0,0 +1,752 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const phase = ref('mark')
|
||||
const isAnimating = ref(false)
|
||||
const currentStep = ref(0)
|
||||
|
||||
const objects = ref([
|
||||
{ id: 1, name: 'obj1', color: '#68d391', marked: false, collected: false },
|
||||
{ id: 2, name: 'obj2', color: '#4299e1', marked: false, collected: false },
|
||||
{ id: 3, name: 'obj3', color: '#ed8936', marked: false, collected: false },
|
||||
{ id: 4, name: 'obj4', color: '#f687b3', marked: false, collected: false },
|
||||
{ id: 5, name: 'obj5', color: '#a3bffa', marked: false, collected: false },
|
||||
{ id: 6, name: 'obj6', color: '#fc8181', marked: false, collected: false }
|
||||
])
|
||||
|
||||
const references = ref([
|
||||
{ from: 'root', to: 1, active: false },
|
||||
{ from: 1, to: 2, active: false },
|
||||
{ from: 1, to: 3, active: false },
|
||||
{ from: 3, to: 4, active: false }
|
||||
])
|
||||
|
||||
const phases = [
|
||||
{ name: 'mark', label: '标记阶段', description: '从根对象开始,标记所有可达对象' },
|
||||
{ name: 'sweep', label: '清除阶段', description: '回收未标记的对象' }
|
||||
]
|
||||
|
||||
const steps = [
|
||||
{ phase: 'mark', action: 'mark-root', description: '从根对象开始标记' },
|
||||
{ phase: 'mark', action: 'mark-1', description: '标记 obj1 (根对象引用)' },
|
||||
{ phase: 'mark', action: 'mark-2', description: '标记 obj2 (obj1 引用)' },
|
||||
{ phase: 'mark', action: 'mark-3', description: '标记 obj3 (obj1 引用)' },
|
||||
{ phase: 'mark', action: 'mark-4', description: '标记 obj4 (obj3 引用)' },
|
||||
{ phase: 'sweep', action: 'collect-5', description: '回收 obj5 (未标记)' },
|
||||
{ phase: 'sweep', action: 'collect-6', description: '回收 obj6 (未标记)' },
|
||||
{ phase: 'done', action: 'finish', description: '垃圾回收完成' }
|
||||
]
|
||||
|
||||
const reset = () => {
|
||||
currentStep.value = 0
|
||||
phase.value = 'mark'
|
||||
isAnimating.value = false
|
||||
objects.value.forEach(obj => {
|
||||
obj.marked = false
|
||||
obj.collected = false
|
||||
})
|
||||
references.value.forEach(ref => {
|
||||
ref.active = false
|
||||
})
|
||||
}
|
||||
|
||||
const nextStep = () => {
|
||||
if (currentStep.value >= steps.length) return
|
||||
|
||||
const step = steps[currentStep.value]
|
||||
|
||||
switch (step.action) {
|
||||
case 'mark-root':
|
||||
references.value[0].active = true
|
||||
break
|
||||
case 'mark-1':
|
||||
objects.value[0].marked = true
|
||||
references.value[1].active = true
|
||||
references.value[2].active = true
|
||||
break
|
||||
case 'mark-2':
|
||||
objects.value[1].marked = true
|
||||
break
|
||||
case 'mark-3':
|
||||
objects.value[2].marked = true
|
||||
references.value[3].active = true
|
||||
break
|
||||
case 'mark-4':
|
||||
objects.value[3].marked = true
|
||||
phase.value = 'sweep'
|
||||
break
|
||||
case 'collect-5':
|
||||
objects.value[4].collected = true
|
||||
break
|
||||
case 'collect-6':
|
||||
objects.value[5].collected = true
|
||||
phase.value = 'done'
|
||||
break
|
||||
case 'finish':
|
||||
phase.value = 'done'
|
||||
break
|
||||
}
|
||||
|
||||
currentStep.value++
|
||||
}
|
||||
|
||||
const play = async () => {
|
||||
if (isAnimating.value) return
|
||||
isAnimating.value = true
|
||||
reset()
|
||||
|
||||
while (currentStep.value < steps.length && isAnimating.value) {
|
||||
nextStep()
|
||||
await new Promise(resolve => setTimeout(resolve, 1200))
|
||||
}
|
||||
|
||||
isAnimating.value = false
|
||||
}
|
||||
|
||||
const stop = () => {
|
||||
isAnimating.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="garbage-collection-demo">
|
||||
<h3>垃圾回收机制</h3>
|
||||
|
||||
<!-- 阶段指示器 -->
|
||||
<div class="phase-indicator">
|
||||
<div class="phase-tabs">
|
||||
<div
|
||||
v-for="p in phases"
|
||||
:key="p.name"
|
||||
:class="{ 'active': phase === p.name }"
|
||||
class="phase-tab"
|
||||
>
|
||||
<span class="phase-label">{{ p.label }}</span>
|
||||
<span class="phase-description">{{ p.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对象关系图 -->
|
||||
<div class="graph-container">
|
||||
<div class="graph-header">
|
||||
<h4>对象引用关系</h4>
|
||||
<div class="legend">
|
||||
<div class="legend-item">
|
||||
<span class="legend-color unmarked"></span>
|
||||
<span>未标记</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color marked"></span>
|
||||
<span>已标记(可达)</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color collected"></span>
|
||||
<span>已回收</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="object-graph">
|
||||
<!-- 根对象 -->
|
||||
<div class="root-object">
|
||||
<div class="object-box root">
|
||||
<div class="object-icon">🌳</div>
|
||||
<div class="object-name">Root</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对象节点 -->
|
||||
<div class="objects-grid">
|
||||
<div
|
||||
v-for="obj in objects"
|
||||
:key="obj.id"
|
||||
class="object-node"
|
||||
:class="{
|
||||
'marked': obj.marked,
|
||||
'collected': obj.collected
|
||||
}"
|
||||
>
|
||||
<div class="object-box" :style="{ borderColor: obj.color }">
|
||||
<div class="object-icon" :style="{ background: obj.color }">
|
||||
{{ obj.collected ? '💀' : '📦' }}
|
||||
</div>
|
||||
<div class="object-name">{{ obj.name }}</div>
|
||||
<div v-if="obj.marked" class="object-status">✓ 可达</div>
|
||||
<div v-if="obj.collected" class="object-status collected">✗ 回收</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引用连线 (用SVG绘制) -->
|
||||
<svg class="connections" viewBox="0 0 600 400">
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#a0aec0" />
|
||||
</marker>
|
||||
</defs>
|
||||
<!-- Root -> obj1 -->
|
||||
<line
|
||||
x1="80"
|
||||
y1="200"
|
||||
x2="180"
|
||||
y2="100"
|
||||
:class="{ 'active': references[0].active }"
|
||||
marker-end="url(#arrowhead)"
|
||||
/>
|
||||
<!-- obj1 -> obj2 -->
|
||||
<line
|
||||
x1="220"
|
||||
y1="120"
|
||||
x2="220"
|
||||
y2="180"
|
||||
:class="{ 'active': references[1].active }"
|
||||
marker-end="url(#arrowhead)"
|
||||
/>
|
||||
<!-- obj1 -> obj3 -->
|
||||
<line
|
||||
x1="260"
|
||||
y1="120"
|
||||
x2="380"
|
||||
y2="120"
|
||||
:class="{ 'active': references[2].active }"
|
||||
marker-end="url(#arrowhead)"
|
||||
/>
|
||||
<!-- obj3 -> obj4 -->
|
||||
<line
|
||||
x1="400"
|
||||
y1="140"
|
||||
x2="400"
|
||||
y2="200"
|
||||
:class="{ 'active': references[3].active }"
|
||||
marker-end="url(#arrowhead)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前步骤说明 -->
|
||||
<div class="step-description">
|
||||
<div class="step-content">
|
||||
<strong>当前操作:</strong>
|
||||
<span v-if="currentStep < steps.length">
|
||||
{{ steps[currentStep].description }}
|
||||
</span>
|
||||
<span v-else>
|
||||
垃圾回收完成
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制按钮 -->
|
||||
<div class="controls">
|
||||
<button @click="play" :disabled="isAnimating" class="btn-play">
|
||||
{{ isAnimating ? '执行中...' : '▶ 自动演示' }}
|
||||
</button>
|
||||
<button @click="nextStep" :disabled="isAnimating || currentStep >= steps.length" class="btn-step">
|
||||
⏭ 单步执行
|
||||
</button>
|
||||
<button @click="stop" :disabled="!isAnimating" class="btn-stop">
|
||||
⏸ 停止
|
||||
</button>
|
||||
<button @click="reset" :disabled="isAnimating" class="btn-reset">
|
||||
🔄 重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 算法说明 -->
|
||||
<div class="algorithm-box">
|
||||
<h4>标记-清除算法 (Mark-and-Sweep)</h4>
|
||||
<div class="algorithm-steps">
|
||||
<div class="algorithm-step">
|
||||
<span class="step-number">1</span>
|
||||
<div class="step-content">
|
||||
<strong>标记阶段</strong>
|
||||
<p>从根对象(Root)开始,遍历所有可达对象,标记为"活动对象"</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="algorithm-step">
|
||||
<span class="step-number">2</span>
|
||||
<div class="step-content">
|
||||
<strong>清除阶段</strong>
|
||||
<p>遍历整个堆内存,回收所有未被标记的对象</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="algorithm-step">
|
||||
<span class="step-number">3</span>
|
||||
<div class="step-content">
|
||||
<strong>重置标记</strong>
|
||||
<p>清除所有标记位,为下一次垃圾回收做准备</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="key-points">
|
||||
<h5>核心要点</h5>
|
||||
<ul>
|
||||
<li><strong>根对象(Root):</strong> 全局变量、栈上的变量等,总是被认为是可达的</li>
|
||||
<li><strong>可达对象:</strong> 从根对象出发,通过引用链能访问到的对象</li>
|
||||
<li><strong>垃圾对象:</strong> 无法从根对象访问到的对象,会被回收</li>
|
||||
<li><strong>循环引用:</strong> 如果两个对象互相引用但都不可达,仍会被回收</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 实际应用 -->
|
||||
<div class="practical-tips">
|
||||
<h4>实际应用技巧</h4>
|
||||
<div class="tips-grid">
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">💡</div>
|
||||
<div class="tip-content">
|
||||
<strong>及时解除引用</strong>
|
||||
<p>对象不再使用时,将其设为 null</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">🔒</div>
|
||||
<div class="tip-content">
|
||||
<strong>避免意外的全局变量</strong>
|
||||
<p>使用 const/let 代替 var</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">🧹</div>
|
||||
<div class="tip-content">
|
||||
<strong>清理事件监听</strong>
|
||||
<p>组件销毁时移除所有监听器</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-card">
|
||||
<div class="tip-icon">📊</div>
|
||||
<div class="tip-content">
|
||||
<strong>定期检查内存</strong>
|
||||
<p>用 DevTools Memory 面板监控</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.garbage-collection-demo {
|
||||
border: 1px solid var(--vp-c-border);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
margin: 24px 0;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.phase-indicator {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.phase-tabs {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.phase-tab {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.phase-tab.active {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background: rgba(62, 175, 124, 0.1);
|
||||
}
|
||||
|
||||
.phase-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.phase-description {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.graph-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.graph-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.legend-color {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid;
|
||||
}
|
||||
|
||||
.legend-color.unmarked {
|
||||
background: var(--vp-c-bg);
|
||||
border-color: var(--vp-c-border);
|
||||
}
|
||||
|
||||
.legend-color.marked {
|
||||
background: rgba(104, 217, 145, 0.2);
|
||||
border-color: #68d391;
|
||||
}
|
||||
|
||||
.legend-color.collected {
|
||||
background: rgba(245, 101, 101, 0.2);
|
||||
border-color: #f56565;
|
||||
}
|
||||
|
||||
.object-graph {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.root-object {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.objects-grid {
|
||||
position: absolute;
|
||||
left: 150px;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.object-node {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.object-box {
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 3px solid var(--vp-c-border);
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.object-box.root {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background: rgba(62, 175, 124, 0.1);
|
||||
}
|
||||
|
||||
.object-node.marked .object-box {
|
||||
border-color: #68d391;
|
||||
background: rgba(104, 217, 145, 0.1);
|
||||
}
|
||||
|
||||
.object-node.collected .object-box {
|
||||
border-color: #f56565;
|
||||
background: rgba(245, 101, 101, 0.1);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.object-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0 auto 12px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.object-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.object-status {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.object-status:not(.collected) {
|
||||
color: #68d391;
|
||||
}
|
||||
|
||||
.object-status.collected {
|
||||
color: #f56565;
|
||||
}
|
||||
|
||||
.connections {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.connections line {
|
||||
stroke: #a0aec0;
|
||||
stroke-width: 2;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.connections line.active {
|
||||
stroke: var(--vp-c-brand-1);
|
||||
stroke-width: 3;
|
||||
animation: pulse 1s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.step-description {
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-left: 4px solid var(--vp-c-brand-1);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.step-content strong {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.btn-play {
|
||||
background: var(--vp-c-brand-1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-play:hover:not(:disabled) {
|
||||
background: var(--vp-c-brand-2);
|
||||
}
|
||||
|
||||
.btn-step {
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.btn-step:hover:not(:disabled) {
|
||||
background: var(--vp-c-bg-soft-hover);
|
||||
}
|
||||
|
||||
.btn-stop {
|
||||
background: #ed8936;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-stop:hover:not(:disabled) {
|
||||
background: #dd6b20;
|
||||
}
|
||||
|
||||
.btn-reset {
|
||||
background: #f56565;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-reset:hover:not(:disabled) {
|
||||
background: #e53e3e;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.algorithm-box {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.algorithm-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.algorithm-step {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--vp-c-brand-1);
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.algorithm-step .step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.algorithm-step strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.algorithm-step p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.key-points {
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.key-points ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.key-points li {
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.key-points strong {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.practical-tips {
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tips-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tip-card {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.tip-icon {
|
||||
font-size: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tip-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tip-content strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.tip-content p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user