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

414 lines
9.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
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.
<!--
SignalsDemo.vue
信号机制演示组件
用途
演示进程控制信号Signals如何工作特别是 `Ctrl+C` `Ctrl+Z`
说明这些组合键不是发送字符而是触发操作系统级别的中断信号
交互功能
- 模拟运行点击按钮启动一个模拟进程 `sleep 100`
- 发送信号点击按钮或快捷键发送 SIGINT/SIGTSTP
- 状态反馈展示进程状态的变化运行中 -> 被杀死/被挂起
-->
<template>
<div class="signals-demo">
<div class="left-panel">
<div class="signal-list">
<div
class="signal-item"
:class="{ active: activeSignal === 'SIGINT' }"
@click="sendSignal('SIGINT')"
>
<div class="key-combo">
<span class="key">Ctrl</span>+<span class="key">C</span>
<span class="action">Interrupt</span>
</div>
<div class="signal-name">SIGINT</div>
</div>
<div
class="signal-item"
:class="{ active: activeSignal === 'SIGTSTP' }"
@click="sendSignal('SIGTSTP')"
>
<div class="key-combo">
<span class="key">Ctrl</span>+<span class="key">Z</span>
<span class="action">Suspend</span>
</div>
<div class="signal-name">SIGTSTP</div>
</div>
</div>
<div class="info-box">
<div v-if="activeSignal === 'SIGINT'">
<div class="info-header">
<span class="highlight">Ctrl+C</span>
<span class="signal-green">SIGINT</span>
</div>
<div class="info-desc">Stop the running program</div>
<p>
Sends SIGINT (signal interrupt) to the foreground process. Most
programs respond by stopping immediately. It's how you cancel a
long-running command or exit a program that's stuck.
</p>
<div class="example-box">
Example: Running `sleep 100` and pressing Ctrl+C stops it
immediately.
</div>
</div>
<div v-else-if="activeSignal === 'SIGTSTP'">
<div class="info-header">
<span class="highlight">Ctrl+Z</span>
<span class="signal-blue">SIGTSTP</span>
</div>
<div class="info-desc">Suspend the running program</div>
<p>
Sends SIGTSTP (signal terminal stop). The process is paused and put
in the background. You can resume it later with `fg` command.
</p>
<div class="example-box">
Example: Pressing Ctrl+Z pauses a running editor like vim, returning
you to the shell.
</div>
</div>
<div v-else>
<div class="info-header">Select a signal</div>
<p>Click on a signal type above to see how it works.</p>
</div>
</div>
</div>
<div class="right-panel">
<div class="terminal-window">
<div class="window-header">
<div class="dots"><span></span><span></span><span></span></div>
</div>
<div class="window-content">
<div
v-for="(line, i) in lines"
:key="i"
class="term-line"
:class="line.type"
>
{{ line.text }}
</div>
<div v-if="isRunning" class="term-line output">sleeping...</div>
<div v-if="inputBuffer" class="term-line input">
<span class="prompt">$</span> {{ inputBuffer
}}<span class="cursor"></span>
</div>
<div v-else class="term-line input">
<span class="prompt">$</span> <span class="cursor"></span>
</div>
</div>
</div>
<div class="controls">
<button class="btn" @click="runCommand" :disabled="isRunning">
Run Command
</button>
<button class="btn" @click="sendSignal('SIGINT')">Ctrl+C</button>
<button class="btn" @click="sendSignal('SIGTSTP')">Ctrl+Z</button>
<button class="btn secondary" @click="reset">Reset</button>
</div>
<div class="state-display">
State: <span :class="stateClass">{{ processState }}</span>
</div>
<p class="instruction">
Click "Run Command" to start a simulated process, then try sending
different signals.
</p>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const activeSignal = ref('SIGINT')
const isRunning = ref(false)
const lines = ref([{ type: 'input', text: '$ sleep 100' }])
const processState = ref('Running')
const inputBuffer = ref('')
const stateClass = computed(() => {
if (processState.value.includes('Running')) return 'state-green'
if (processState.value.includes('interrupted')) return 'state-red'
if (processState.value.includes('suspended')) return 'state-blue'
return ''
})
const runCommand = () => {
reset()
isRunning.value = true
processState.value = 'Running (PID 1234)'
}
const sendSignal = (sig) => {
activeSignal.value = sig
if (!isRunning.value && sig === 'SIGINT') return
if (sig === 'SIGINT') {
lines.value.push({ type: 'output', text: 'sleeping...' })
lines.value.push({ type: 'control', text: '^C' })
isRunning.value = false
processState.value = 'Process interrupted (killed)'
} else if (sig === 'SIGTSTP') {
lines.value.push({ type: 'output', text: 'sleeping...' })
lines.value.push({ type: 'control', text: '^Z' })
lines.value.push({
type: 'output',
text: '[1]+ Stopped sleep 100'
})
isRunning.value = false
processState.value = 'Process suspended (stopped)'
}
}
const reset = () => {
lines.value = [{ type: 'input', text: '$ sleep 100' }]
isRunning.value = true
processState.value = 'Running (PID 1234)'
}
// Initial state
reset()
</script>
<style scoped>
.signals-demo {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(280px, 1fr)
); /* 自动适应宽度,不够时换行 */
gap: 30px;
background: #09090b;
padding: 30px;
border-radius: 12px;
border: 1px solid #27272a;
font-family: 'JetBrains Mono', 'Menlo', monospace;
color: #e4e4e7;
overflow: hidden; /* 防止溢出 */
}
.left-panel {
display: flex;
flex-direction: column;
gap: 20px;
min-width: 0; /* 防止 flex 子项溢出 */
}
.signal-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background: #18181b;
border: 1px solid #27272a;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
}
.signal-item:hover {
border-color: #52525b;
}
.signal-item.active {
background: #27272a;
border-left: 3px solid #facc15;
}
.key-combo {
display: flex;
align-items: center;
gap: 5px;
}
.key {
color: #facc15;
font-weight: bold;
}
.action {
color: #a1a1aa;
margin-left: 10px;
font-size: 13px;
}
.signal-name {
color: #22d3ee;
font-weight: bold;
}
.info-box {
background: #18181b;
padding: 20px;
border-radius: 6px;
border: 1px solid #27272a;
font-size: 14px;
line-height: 1.6;
}
.info-header {
font-size: 18px;
margin-bottom: 10px;
font-weight: bold;
}
.highlight {
color: #facc15;
}
.signal-green {
color: #22c55e;
}
.signal-blue {
color: #3b82f6;
}
.info-desc {
color: #a1a1aa;
margin-bottom: 15px;
}
.example-box {
background: #000;
padding: 10px;
border-radius: 4px;
font-size: 13px;
color: #d4d4d8;
margin-top: 15px;
border: 1px solid #27272a;
}
.right-panel {
display: flex;
flex-direction: column;
gap: 20px;
min-width: 0; /* 防止 flex 子项溢出 */
}
.terminal-window {
background: #000;
border: 1px solid #27272a;
border-radius: 8px;
height: 200px;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.window-header {
padding: 10px 15px;
border-bottom: 1px solid #27272a;
background: #18181b;
}
.dots span {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
background: #3f3f46;
margin-right: 6px;
}
.window-content {
padding: 15px;
flex: 1;
font-size: 14px;
overflow-y: auto;
}
.term-line {
margin-bottom: 5px;
}
.control {
color: #ef4444;
}
.output {
color: #d4d4d8;
}
.input {
color: #fff;
}
.prompt {
color: #71717a;
margin-right: 8px;
}
.cursor {
display: inline-block;
width: 8px;
height: 14px;
background: #a1a1aa;
vertical-align: middle;
}
.controls {
display: flex;
gap: 10px;
flex-wrap: wrap; /* 允许按钮换行 */
}
.btn {
background: #18181b;
border: 1px solid #27272a;
color: #e4e4e7;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
flex: 1;
white-space: nowrap; /* 防止文字换行 */
min-width: 80px; /* 最小宽度 */
transition: all 0.2s;
font-size: 13px;
}
.btn:hover:not(:disabled) {
background: #27272a;
border-color: #52525b;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.state-display {
font-size: 16px;
margin-top: 10px;
}
.state-green {
color: #22c55e;
}
.state-red {
color: #ef4444;
}
.state-blue {
color: #3b82f6;
}
.instruction {
color: #a1a1aa;
font-size: 13px;
}
@media (max-width: 640px) {
.signals-demo {
padding: 20px;
gap: 20px;
}
}
</style>