feat: extend linux operations curriculum

This commit is contained in:
Codex
2026-03-19 15:25:19 +08:00
parent f61376aa8a
commit 8c5aafbe8b
2 changed files with 279 additions and 3 deletions

View File

@@ -288,6 +288,27 @@
color: var(--muted);
line-height: 1.7;
}
.stage-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-top: 14px;
}
.stage-card {
padding: 14px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255,255,255,0.18);
}
.stage-card h4 {
margin: 0 0 8px;
font-size: 16px;
}
.stage-card p {
margin: 0 0 8px;
color: var(--muted);
line-height: 1.7;
}
.lesson-btn.done {
border-color: rgba(29, 155, 108, 0.4);
background: rgba(29, 155, 108, 0.1);
@@ -305,12 +326,14 @@
.hero-grid, .detail-grid { grid-template-columns: 1fr; }
.mini-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.module-summary-grid { grid-template-columns: 1fr; }
.stage-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
.shell { padding: 14px; }
.topbar, .hero-head, .search, .terminal-input { flex-direction: column; align-items: stretch; }
.stats { grid-template-columns: 1fr; }
.mini-stats { grid-template-columns: 1fr; }
.stage-grid { grid-template-columns: 1fr; }
}
</style>
</head>
@@ -408,6 +431,13 @@
<div class="mastery-note" id="masteryNote">Master a lesson after you can explain the command, predict the output, and connect it to a real operations step.</div>
</section>
<section class="detail card">
<div class="eyebrow">Learning stages</div>
<h2 style="margin: 8px 0 0;">System route from Linux basics to operations habits</h2>
<p class="muted">This view helps you study in phases instead of bouncing between unrelated commands.</p>
<div class="stage-grid" id="stageGrid"></div>
</section>
<section class="detail card">
<div class="eyebrow">Module heatmap</div>
<h2 style="margin: 8px 0 0;">See how the learning path is distributed</h2>
@@ -567,6 +597,7 @@
renderRuntime(state.overview.runtime || {});
renderCommandTags(state.overview.commands || []);
renderModules();
renderStageGrid();
renderModuleSummaryGrid();
}
@@ -628,6 +659,34 @@
`).join('');
}
function renderStageGrid() {
const groups = [
{ title: 'Stage 1: Foundations', summary: 'Orientation, listing, file operations, and permissions.', range: [0, 1] },
{ title: 'Stage 2: Search & Observation', summary: 'Text search, log preview, process, service, and network inspection.', range: [2, 3] },
{ title: 'Stage 3: Incidents', summary: 'Disk, auth, and service-path drills that connect commands into playbooks.', range: [4, 4] },
{ title: 'Stage 4: Automation & Platform', summary: 'Shell state, package tools, archives, monitoring, and scheduling.', range: [5, 7] }
];
const modules = state.overview.modules || [];
const target = document.getElementById('stageGrid');
target.innerHTML = groups.map((group) => {
const selectedModules = modules.slice(group.range[0], group.range[1] + 1);
const lessonTotal = selectedModules.reduce((sum, item) => sum + (item.lesson_count || 0), 0);
const exerciseTotal = selectedModules.reduce((sum, item) => sum + (item.exercise_count || 0), 0);
return `
<article class="stage-card">
<h4>${escapeHtml(group.title)}</h4>
<p>${escapeHtml(group.summary)}</p>
<div class="mini-stats">
<div class="mini-stat"><span>Modules</span><strong>${selectedModules.length}</strong></div>
<div class="mini-stat"><span>Lessons</span><strong>${lessonTotal}</strong></div>
<div class="mini-stat"><span>Exercises</span><strong>${exerciseTotal}</strong></div>
<div class="mini-stat"><span>Mastered</span><strong>${selectedModules.reduce((sum, item) => sum + (item.lessons || []).filter((lesson) => isMastered(lesson.id)).length, 0)}</strong></div>
</div>
</article>
`;
}).join('');
}
async function openLesson(lessonId, focusExerciseId = '') {
const response = await fetch('/api/lesson?id=' + encodeURIComponent(lessonId));
const payload = await response.json();