+
+
+
+ 👆
+ 不断点击屏幕演示 / Keep Clicking
+
+
+
+
+
+
+ ✅
+ 演示结束,点击重置 / Finished (Reset)
+
+
+
+
+
+
TERMINAL (终端)
@@ -181,8 +210,8 @@ const steps = [
{
titleEn: "3. Shell Parsing",
titleZh: "3. Shell 解析",
- descEn: "The Shell receives the characters and figures out what you want.",
- descZh: "Shell 接收到字符,并解析你的意图。",
+ descEn: "The Shell (Waiter) translates your command for the Kernel.",
+ descZh: "Shell(服务员)接收指令,并将其翻译成内核能听懂的请求。",
techEn: "Shell tokenizes input, finds the 'ls' executable in $PATH.",
techZh: "Shell 对输入进行分词,并在 $PATH 环境变量中查找 'ls' 可执行文件。",
action: async () => {
@@ -207,8 +236,8 @@ const steps = [
{
titleEn: "5. Kernel Execution",
titleZh: "5. 内核执行",
- descEn: "The Kernel (the boss) talks to the hardware to get the actual data.",
- descZh: "内核(大管家)与硬件通信以获取实际数据。",
+ descEn: "The Kernel (Kitchen) executes the request by accessing hardware.",
+ descZh: "内核(后厨)直接操作硬件(如磁盘)来执行实际任务。",
techEn: "Kernel driver accesses the file system (APFS/ext4).",
techZh: "内核驱动程序访问文件系统 (APFS/ext4)。",
action: async () => {
@@ -366,9 +395,169 @@ const reset = () => {
justify-content: space-between;
align-items: center;
position: relative;
- padding: 0 10px;
+ /* Increase padding to accommodate labels */
+ padding: 40px 10px 20px 10px;
+ z-index: 1;
+ cursor: default;
+ transition: background 0.3s;
}
+.diagram-container.clickable {
+ cursor: pointer;
+}
+
+.diagram-container.clickable:hover {
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.click-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 50; /* Topmost */
+ background: rgba(0, 0, 0, 0.4);
+ backdrop-filter: blur(2px);
+ border-radius: 12px;
+ animation: pulse-bg 2s infinite;
+}
+
+.click-hint {
+ background: #22c55e;
+ color: #000;
+ padding: 10px 20px;
+ border-radius: 30px;
+ font-weight: bold;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ box-shadow: 0 4px 15px rgba(34, 197, 94, 0.4);
+ transform: scale(1);
+ transition: transform 0.2s;
+}
+
+.diagram-container:hover .click-hint {
+ transform: scale(1.05);
+}
+
+@keyframes pulse-bg {
+ 0% { background: rgba(0, 0, 0, 0.4); }
+ 50% { background: rgba(0, 0, 0, 0.2); }
+ 100% { background: rgba(0, 0, 0, 0.4); }
+}
+
+.completed-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 50; /* Same as click overlay */
+ background: rgba(0, 0, 0, 0.6);
+ backdrop-filter: blur(2px);
+ animation: fade-in 0.5s;
+}
+
+.completed-hint {
+ background: #10b981;
+ color: #fff;
+ padding: 10px 20px;
+ border-radius: 30px;
+ font-weight: bold;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);
+ cursor: pointer;
+ transition: transform 0.2s;
+}
+
+.completed-hint:hover {
+ transform: scale(1.05);
+ background: #059669;
+}
+
+.spaces-bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ z-index: 0;
+ pointer-events: none;
+}
+
+.space {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.space-header {
+ font-size: 12px;
+ font-weight: bold;
+ text-transform: uppercase;
+ padding: 8px;
+ opacity: 0.7;
+}
+
+.user-space {
+ flex: 2;
+ background: rgba(34, 211, 238, 0.03);
+ border-right: 1px dashed #3f3f46;
+ border-radius: 8px 0 0 8px;
+ align-items: flex-start;
+ /* Ensure User Space (containing Shell) is below the Barrier Label */
+ z-index: 0;
+}
+
+.user-space .space-header { color: #22d3ee; }
+
+.kernel-space {
+ flex: 1;
+ background: rgba(239, 68, 68, 0.03);
+ border-radius: 0 8px 8px 0;
+ align-items: flex-end;
+ z-index: 0;
+}
+
+.kernel-space .space-header { color: #ef4444; }
+
+.barrier {
+ width: 2px;
+ background: transparent;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+ z-index: 10; /* Bring Barrier to front */
+}
+
+.barrier-line {
+ width: 2px;
+ height: 100%;
+ background: repeating-linear-gradient(
+ to bottom,
+ #facc15 0,
+ #facc15 10px,
+ transparent 10px,
+ transparent 20px
+ );
+ opacity: 0.3;
+}
+
+
+
.node {
background: #18181b;
border: 2px solid #27272a;
@@ -378,10 +567,15 @@ const reset = () => {
display: flex;
flex-direction: column;
transition: all 0.3s;
- z-index: 2;
+ z-index: 5; /* Nodes should be above spaces but below barrier label if overlapping */
position: relative;
}
+/* Specific z-index for Shell to prevent it from covering barrier label */
+.node.shell {
+ z-index: 1;
+}
+
.node.active {
border-color: #22c55e;
box-shadow: 0 0 15px rgba(34, 197, 94, 0.2);
diff --git a/docs/.vitepress/theme/components/appendix/terminal-intro/BufferSwitchDemo.vue b/docs/.vitepress/theme/components/appendix/terminal-intro/BufferSwitchDemo.vue
new file mode 100644
index 0000000..d69b0f0
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/terminal-intro/BufferSwitchDemo.vue
@@ -0,0 +1,284 @@
+
+
+
+
+
+
+
+ Terminal - Buffer Switching Demo
+
+
+
+
+
+
➜ ls -la
+
total 16
+
drwxr-xr-x 2 user staff 64 Jan 15 10:00 .
+
drwxr-xr-x 4 user staff 128 Jan 15 09:55 ..
+
-rw-r--r-- 1 user staff 1024 Jan 15 10:00 notes.txt
+
➜ echo "Hello World"
+
Hello World
+
➜ vim notes.txt
+
+
+
+
+
+
+
+
+
1
This is a text file opened in Vim.
+
2
+
3
Notice how this interface takes up
+
4
the entire screen?
+
5
+
6
It is running in the Alternate Buffer.
+
~
+
~
+
+
+ NORMAL
+ notes.txt [unix] (10:24)
+
+
+ :q
+
+
+
+
+
+
+
+
+
+
Current: Primary Buffer (主缓冲区)
+
This is the standard scrolling log. Commands are executed line by line.
+
+
+
+
Current: Alternate Buffer (备用缓冲区)
+
A separate "canvas" for full-screen apps. It hides the history but doesn't delete it.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/.vitepress/theme/components/appendix/terminal-intro/CookedRawDemo.vue b/docs/.vitepress/theme/components/appendix/terminal-intro/CookedRawDemo.vue
new file mode 100644
index 0000000..de7317b
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/terminal-intro/CookedRawDemo.vue
@@ -0,0 +1,362 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
⬇
+
+
+
+
+ 2. Line Buffer (Kernel)
+ Active
+ Bypassed
+
+
+
+ {{ char }}
+ _
+
+
+ ⚡ Direct Pass-through ⚡
+
+
+
+ Waiting for Enter... (Backspace works)
+ No buffering. Every key is sent immediately.
+
+
+
+
⬇
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/.vitepress/theme/components/appendix/terminal-intro/EscapeParserDemo.vue b/docs/.vitepress/theme/components/appendix/terminal-intro/EscapeParserDemo.vue
new file mode 100644
index 0000000..53e1545
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/terminal-intro/EscapeParserDemo.vue
@@ -0,0 +1,418 @@
+
+
+
+
+
+
+
Input Byte Stream / 输入字节流
+
+
+
+
+ {{ char.display }}
+ {{ char.hex }}
+
+
+
+
+
+
+
+
+
+
+
+
NORMAL
+
Print Characters
+
+
→
+
+
ESCAPE MODE
+
Buffer Command...
+
+
+
+
+ ⚡
+ {{ lastAction }}
+
+
+
+
+
+
Terminal Screen / 屏幕显示
+
+ {{ char.val }}_
+
+
+
+
+
+ Normal 模式下,字符直接上屏。
+ Escape 模式下(遇到 ESC 后),终端停止输出,开始收集字符作为指令,直到指令结束(如 m)并执行。
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalDefinition.vue b/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalDefinition.vue
index 7f095dd..7cacb0e 100644
--- a/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalDefinition.vue
+++ b/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalDefinition.vue
@@ -82,11 +82,12 @@
Event Loop / 事件循环
- {{ guiEvent.type }}
+ {{ ev.type }}
@@ -135,7 +136,7 @@ const mode = ref('cli') // 'cli' | 'gui'
const isAnimating = ref(false)
const activeChars = ref([])
const typedContent = ref('')
-const demoText = 'ls -la'
+const demoText = 'echo "hello world"'
const startSimulation = () => {
if (isAnimating.value) return
@@ -168,7 +169,7 @@ const startSimulation = () => {
// Animate this char
let progress = 10
const interval = setInterval(() => {
- progress += 2
+ progress += 4 // Faster speed
const charObj = activeChars.value.find(c => c.id === charId)
if (charObj) charObj.progress = progress
@@ -180,7 +181,7 @@ const startSimulation = () => {
// Next char
index++
- setTimeout(processNextChar, 300)
+ setTimeout(processNextChar, 100) // Faster typing
}
}, 20)
}
@@ -191,60 +192,92 @@ const startSimulation = () => {
// GUI Logic
const isGuiAnimating = ref(false)
const isGuiClicking = ref(false)
-const guiEvent = ref(null)
+const guiEvents = ref([]) // Array of events
const iconSelected = ref(false)
-const cursorPosition = ref({ x: 50, y: 50 })
+const inputMousePosition = ref({ x: 80, y: 60 }) // Input side (Invisible physical mouse)
+const screenCursorPosition = ref({ x: 80, y: 60 }) // Output side (Visible screen cursor)
const cursorStyle = computed(() => ({
- transform: `translate(${cursorPosition.value.x}px, ${cursorPosition.value.y}px)`
+ transform: `translate(${screenCursorPosition.value.x}px, ${screenCursorPosition.value.y}px)`
}))
const startGuiSimulation = () => {
if (isGuiAnimating.value) return
isGuiAnimating.value = true
iconSelected.value = false
- cursorPosition.value = { x: 80, y: 60 } // Reset pos
+ inputMousePosition.value = { x: 80, y: 60 }
+ screenCursorPosition.value = { x: 80, y: 60 }
+ guiEvents.value = []
- // 1. Move Cursor
+ // 1. Move Cursor (Physical Mouse Movement)
let step = 0
const moveInterval = setInterval(() => {
step++
- cursorPosition.value = {
+ inputMousePosition.value = {
x: 80 - step * 2,
y: 60 - step * 1.5
}
+ // Emit Move Event frequently (Simulate high polling rate)
+ if (step % 2 === 0) {
+ const targetX = inputMousePosition.value.x
+ const targetY = inputMousePosition.value.y
+ emitGuiEvent(`Move(${Math.round(targetX)},${Math.round(targetY)})`, () => {
+ // When packet arrives: Update screen cursor
+ screenCursorPosition.value = { x: targetX, y: targetY }
+ })
+ }
+
if (step >= 20) {
clearInterval(moveInterval)
// 2. Click
performClick()
}
- }, 20)
+ }, 50)
+}
+
+const emitGuiEvent = (type, onArrive) => {
+ const eventId = Date.now() + Math.random()
+ const newEvent = {
+ id: eventId,
+ type: type,
+ progress: 10
+ }
+ guiEvents.value.push(newEvent)
+
+ let progress = 10
+ const packetInterval = setInterval(() => {
+ progress += 2
+ const ev = guiEvents.value.find(e => e.id === eventId)
+ if (ev) ev.progress = progress
+
+ if (progress >= 90) {
+ clearInterval(packetInterval)
+ guiEvents.value = guiEvents.value.filter(e => e.id !== eventId)
+
+ // Execute callback when packet arrives at Output
+ if (onArrive) onArrive()
+ }
+ }, 10)
}
const performClick = () => {
setTimeout(() => {
isGuiClicking.value = true
- // Send Event Packet
- guiEvent.value = { type: 'Click(40,30)', progress: 10 }
-
- let progress = 10
- const packetInterval = setInterval(() => {
- progress += 2
- if (guiEvent.value) guiEvent.value.progress = progress
+ // Send Click Event
+ emitGuiEvent('Click(40,30)', () => {
+ // When packet arrives: Select icon
+ iconSelected.value = true
- if (progress >= 90) {
- clearInterval(packetInterval)
- guiEvent.value = null
+ setTimeout(() => {
+ isGuiAnimating.value = false
+ }, 1000)
+ })
+
+ setTimeout(() => {
isGuiClicking.value = false
- iconSelected.value = true // Effect
-
- setTimeout(() => {
- isGuiAnimating.value = false
- }, 1000)
- }
- }, 10)
+ }, 200) // Input click feedback is fast
}, 300)
}
@@ -405,7 +438,7 @@ const performClick = () => {
top: 0;
left: 0;
pointer-events: none;
- transition: transform 0.05s linear;
+ transition: transform 0.1s linear; /* Smooth interpolation */
filter: drop-shadow(0 1px 1px rgba(0,0,0,0.5));
}
diff --git a/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalOSDemo.vue b/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalOSDemo.vue
new file mode 100644
index 0000000..715f850
--- /dev/null
+++ b/docs/.vitepress/theme/components/appendix/terminal-intro/TerminalOSDemo.vue
@@ -0,0 +1,618 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ currentOSConfig.title }}
+
+
+
+
+
+
+
+
+ 👆
+ 不断点击屏幕演示 / Keep Clicking
+
+
+
+
+
+
+ ✅
+ 演示结束,点击重置 / Finished (Reset)
+
+
+
+
+
+ {{ line.prompt }}{{ line.content }}
+
+
+ {{ line.content }}
+
+
+
+
+
+ {{ currentOSConfig.prompt }}
+ _
+ (点击屏幕继续 / Click screen to continue)
+ ⏎
+
+
+
+
+
+ 💡
+ {{ currentExplanation }}
+
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js
index a570ec5..fc7ef57 100644
--- a/docs/.vitepress/theme/index.js
+++ b/docs/.vitepress/theme/index.js
@@ -17,9 +17,14 @@ import EscapeSequences from './components/appendix/terminal-intro/EscapeSequence
import InputVisualizer from './components/appendix/terminal-intro/InputVisualizer.vue'
import SignalsDemo from './components/appendix/terminal-intro/SignalsDemo.vue'
import FlowDiagram from './components/appendix/terminal-intro/FlowDiagram.vue'
+import BufferSwitchDemo from './components/appendix/terminal-intro/BufferSwitchDemo.vue'
import AdvancedTUIDemo from './components/appendix/terminal-intro/AdvancedTUIDemo.vue'
import ArchitectureDemo from './components/appendix/terminal-intro/ArchitectureDemo.vue'
import TerminalDefinition from './components/appendix/terminal-intro/TerminalDefinition.vue'
+import TerminalOSDemo from './components/appendix/terminal-intro/TerminalOSDemo.vue'
+
+import EscapeParserDemo from './components/appendix/terminal-intro/EscapeParserDemo.vue'
+import CookedRawDemo from './components/appendix/terminal-intro/CookedRawDemo.vue'
export default {
extends: DefaultTheme,
@@ -32,12 +37,16 @@ export default {
app.component('TerminalGrid', TerminalGrid)
app.component('CellInspector', CellInspector)
app.component('EscapeSequences', EscapeSequences)
+ app.component('EscapeParserDemo', EscapeParserDemo)
+ app.component('CookedRawDemo', CookedRawDemo)
app.component('InputVisualizer', InputVisualizer)
app.component('SignalsDemo', SignalsDemo)
app.component('FlowDiagram', FlowDiagram)
+ app.component('BufferSwitchDemo', BufferSwitchDemo)
app.component('AdvancedTUIDemo', AdvancedTUIDemo)
app.component('ArchitectureDemo', ArchitectureDemo)
app.component('TerminalDefinition', TerminalDefinition)
+ app.component('TerminalOSDemo', TerminalOSDemo)
},
setup() {
const route = useRoute()
diff --git a/docs/zh-cn/appendix/terminal-intro.md b/docs/zh-cn/appendix/terminal-intro.md
index 808f2f2..2c736e7 100644
--- a/docs/zh-cn/appendix/terminal-intro.md
+++ b/docs/zh-cn/appendix/terminal-intro.md
@@ -4,6 +4,10 @@
## 1. 概念界定:终端是什么? (Definition)
+*不同操作系统下的终端长相不同,**命令方式也不同**。点击下方按钮切换查看,注意观察 Windows 和 Linux 是如何用不同的命令(如 `dir` vs `ls`)做同一件事的:*
+
+
+
在图形用户界面(GUI)普及之前,终端是人类与计算机交互的主要方式。即便在今天,它依然是开发者控制计算机最精确、最高效的工具。
@@ -31,25 +35,28 @@
它不处理复杂的图形、图片或视频,而是专注于**文本信息的交互**。
-## 2. 核心架构:终端与 Shell (The Architecture)
+## 2. 核心架构:三者关系大白话 (The Big Picture)
-初学者常混淆“终端”与“Shell”,理解两者的区别是掌握命令行的关键。它们在计算机历史中演化为两个独立的组件:
+别被专业术语吓跑,其实它们就是三个分工明确的“打工人”:
-- **终端 (Terminal)**:负责“交互”。
- 它是一个负责显示字符、接收键盘输入的**窗口程序**。它本身不懂任何逻辑,只负责传输数据。
- *常见的终端软件:macOS Terminal, iTerm2, Windows Terminal, Hyper.*
+- **终端 (Terminal) —— 只是个“传声筒”**
+ * 它只负责**显示画面**和**接收按键**。
+ * 它本身**没有任何智能**,就像一个显示器或键盘。
+ * *它不管你输入的是命令还是乱码,只管把字显示出来。*
-- **Shell (壳 / 命令解释器)**:负责“逻辑”。
- 它是运行在终端内部的**后端程序**,负责接收你的指令、解析语义、调用系统内核并返回结果。
- *常见的 Shell:bash, zsh, fish, sh.*
+- **Shell (壳) —— 真正的“翻译官”**
+ * 它才是有逻辑的程序。
+ * 它负责**听懂**你的命令(比如 `ls`),把它**翻译**成电脑能听懂的指令,然后指挥内核去干活。
+ * *就像 Siri 或小爱同学,听懂你的话,然后去调动手机功能。*
-**类比理解**:
-如果把计算机操作比作去餐厅点餐:
-- **终端**是**餐桌和菜单**(负责展示信息、提供输入界面,但它自己不会做菜)。
-- **Shell**是**服务员**(负责听取你的点单,交给后厨处理,并把结果端回来)。
-- **内核**是**后厨**(负责真正的执行和运算)。
+- **内核 (Kernel) —— 幕后的“大管家”**
+ * 它是操作系统的核心,只有它能直接控制硬件(硬盘、CPU)。
+ * **Shell 不包含内核**,Shell 只是站在门口喊话的人,内核才是屋里干活的人。
-*下面的演示直观展示了从“点餐”到“上菜”的全过程:*
+**一句话总结流程**:
+你在**终端**(窗口)打字 ➡️ **Shell**(翻译官)听懂并指挥 ➡️ **内核**(大管家)去硬件里干活。
+
+*下面的演示展示了这个过程,注意看 Shell 和内核之间那道“墙”:*
@@ -88,9 +95,13 @@
- 序列 `\033[31m` → **指令**:将后续文字颜色设为红色。
- 序列 `\033[2J` → **指令**:清空屏幕。
-这就是为什么有时候在日志文件中会看到乱码(如 `^[[31m`),那其实是未被正确解析的颜色指令。
+这就好比你和朋友约定:如果我正常说话,你就记录下来;如果我举起左手(相当于 `ESC`),接下来的那句话就是命令而不是内容。
-*下方组件展示了转义序列如何实时改变终端的渲染状态:*
+*点击下方的“播放”按钮,观察终端是如何逐个处理字符流,并识别出隐藏的指令:*
+
+
+
+*下方组件则展示了更多种类的转义序列及其渲染效果:*
@@ -110,21 +121,25 @@
-## 6. 运行模式:加工与原始 (Cooked vs. Raw Mode)
+## 6. 运行模式:打字机 vs 游戏机 (Cooked vs. Raw Mode)
-终端有两种主要的工作模式,决定了输入数据如何被处理。理解这一点能帮你明白为什么 `ls` 命令和 `vim` 编辑器的操作体验完全不同。
+终端有两种截然不同的性格。理解这一点,你就能明白为什么在终端里**打命令**和**玩贪吃蛇**是完全不同的体验。
-- **加工模式 (Cooked Mode / Canonical Mode)**:
- 这是标准 Shell 的默认模式。
- - **行为**:终端会**缓存**你的输入,允许你使用退格键修改。只有当你按下回车键(Enter)后,整行数据才会一次性发送给程序。
- - **类比**:像在写信,写完一整句确认无误后才寄出。
- - *适用场景:日常命令输入。*
+- **加工模式 (Cooked Mode) —— 像打字机**
+ * 这是默认模式。
+ * **行为**:你输入的字符会被终端**暂时扣留**,直到你按下回车键(Enter)。
+ * **好处**:这给了你修改的机会。打错了?按退格键(Backspace)删掉重写,程序根本不知道你之前打错过。
+ * *适用场景:平时敲命令(如 `ls`, `cd`)。*
-- **原始模式 (Raw Mode)**:
- 这是高级交互程序的模式。
- - **行为**:终端不进行任何缓冲或处理,将每一个按键(包括修饰键)**即时**发送给程序。
- - **类比**:像打电玩,按下一个键,角色立刻做出反应。
- - *适用场景:Vim 编辑器、终端游戏、交互式菜单。*
+- **原始模式 (Raw Mode) —— 像游戏手柄**
+ * 这是“高手”模式。
+ * **行为**:你按下的每一个键(包括方向键、Ctrl组合键),都会**瞬间**发送给程序,没有任何缓冲。
+ * **好处**:程序能实时响应你的操作。
+ * *适用场景:玩终端游戏(如贪吃蛇)、使用 Vim 编辑器(一种纯键盘操作的编辑器)。*
+
+*点击下方按钮切换模式,体验“写信”与“打游戏”的不同手感:*
+
+
## 7. 进程控制:信号 (Signals)
@@ -140,15 +155,24 @@
## 8. 高级应用:全屏界面与缓冲区 (Buffers & TUI)
-现代终端应用(TUI, Text User Interface)如 `vim`、`htop` 或 `tmux`,能够像图形软件一样利用整个屏幕,这得益于**备用屏幕缓冲区 (Alternate Screen Buffer)**。
+你有没有发现,当你用 `vim` 编辑文件或者用 `htop` 看系统状态时,它们会占满整个屏幕?而当你退出它们时,屏幕瞬间变回了原来的样子,之前的命令记录完全没变。
-终端通常维护两块“画布”:
-1. **主缓冲区 (Primary Buffer)**:保存命令历史,即我们日常滚动查看的流式界面。
-2. **备用缓冲区 (Alternate Buffer)**:一块独立的、不保存历史的画布,供全屏应用使用。
+这是因为终端有两块“画布”在来回切换:
-当你打开 `vim` 时,终端切换到备用缓冲区;当你退出 `vim` 时,终端切回主缓冲区。这就是为什么退出编辑器后,之前的命令历史依然完好无损地留在屏幕上。
+* **主缓冲区 (Primary Buffer)**:就像**草稿本**。
+ * 你写一行,系统回一行。
+ * 写满了就翻页(滚动),以前写的东西都在上面。
+ * *用于:日常敲命令。*
-
+* **备用缓冲区 (Alternate Buffer)**:就像**黑板**。
+ * 程序把黑板擦干净,在上面画画(全屏显示)。
+ * 不管怎么画,都不会影响你桌子上的草稿本。
+ * 当你退出程序时,就像把黑板收起来,你又回到了草稿本面前。
+ * *用于:Vim, Nano, 游戏等全屏软件。*
+
+*点击下方按钮,体验“草稿本”和“黑板”是如何瞬间切换的:*
+
+
---