feat: add linux course diagnostics
This commit is contained in:
78
index.html
78
index.html
@@ -259,6 +259,35 @@
|
||||
color: var(--muted);
|
||||
line-height: 1.8;
|
||||
}
|
||||
.diagnostic {
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 16px;
|
||||
background: rgba(255,255,255,0.18);
|
||||
color: var(--muted);
|
||||
line-height: 1.8;
|
||||
}
|
||||
.module-summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.module-summary-card {
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
background: rgba(255,255,255,0.18);
|
||||
}
|
||||
.module-summary-card h4 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.module-summary-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);
|
||||
@@ -275,6 +304,7 @@
|
||||
.sidebar { position: static; }
|
||||
.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; }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.shell { padding: 14px; }
|
||||
@@ -332,6 +362,18 @@
|
||||
<h2>Course map</h2>
|
||||
<div class="modules" id="moduleList"><div class="empty">Loading modules...</div></div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="eyebrow">Diagnostics</div>
|
||||
<h2>Course coverage check</h2>
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat"><span>Coverage</span><strong id="coverageRate">0%</strong></div>
|
||||
<div class="mini-stat"><span>Operations</span><strong id="operationCount">0</strong></div>
|
||||
<div class="mini-stat"><span>Reflection</span><strong id="reflectionCount">0</strong></div>
|
||||
<div class="mini-stat"><span>Unsupported</span><strong id="unsupportedCount">0</strong></div>
|
||||
</div>
|
||||
<div class="diagnostic" id="diagnosticNote" style="margin-top: 12px;">Checking course and sandbox alignment...</div>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
@@ -366,6 +408,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">Module heatmap</div>
|
||||
<h2 style="margin: 8px 0 0;">See how the learning path is distributed</h2>
|
||||
<p class="muted">This panel helps you check whether the project really teaches both command basics and operations workflows.</p>
|
||||
<div class="module-summary-grid" id="moduleSummaryGrid"></div>
|
||||
</section>
|
||||
|
||||
<section class="detail card">
|
||||
<div class="eyebrow">Learning cockpit</div>
|
||||
<h2 style="margin: 8px 0 0;">Visual study map for the current lesson</h2>
|
||||
@@ -507,10 +556,18 @@
|
||||
document.getElementById('commandCount').textContent = meta.command_count || 0;
|
||||
document.getElementById('masteredCount').textContent = masteredCount();
|
||||
document.getElementById('completionRate').textContent = completionRate() + '%';
|
||||
document.getElementById('coverageRate').textContent = (state.overview.diagnostics?.coverage_rate ?? 0) + '%';
|
||||
document.getElementById('operationCount').textContent = state.overview.diagnostics?.operation_exercises ?? 0;
|
||||
document.getElementById('reflectionCount').textContent = state.overview.diagnostics?.reflection_exercises ?? 0;
|
||||
document.getElementById('unsupportedCount').textContent = state.overview.diagnostics?.unsupported_count ?? 0;
|
||||
document.getElementById('diagnosticNote').textContent = (state.overview.diagnostics?.unsupported_count ?? 0) === 0
|
||||
? 'All commands referenced by the course are covered by the sandbox command set.'
|
||||
: 'Some course commands are not fully supported by the sandbox yet.';
|
||||
document.getElementById('metaLine').textContent = `Version ${meta.version || '4.0'} | Updated ${meta.updated || '--'}`;
|
||||
renderRuntime(state.overview.runtime || {});
|
||||
renderCommandTags(state.overview.commands || []);
|
||||
renderModules();
|
||||
renderModuleSummaryGrid();
|
||||
}
|
||||
|
||||
function renderRuntime(runtime) {
|
||||
@@ -550,6 +607,27 @@
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function renderModuleSummaryGrid() {
|
||||
const modules = state.overview.modules || [];
|
||||
const target = document.getElementById('moduleSummaryGrid');
|
||||
if (!modules.length) {
|
||||
target.innerHTML = '<div class="empty">No module diagnostics found.</div>';
|
||||
return;
|
||||
}
|
||||
target.innerHTML = modules.map((module) => `
|
||||
<article class="module-summary-card">
|
||||
<h4>${escapeHtml(module.display_title)}</h4>
|
||||
<p>${escapeHtml(module.display_summary)}</p>
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat"><span>Lessons</span><strong>${module.lesson_count || 0}</strong></div>
|
||||
<div class="mini-stat"><span>Exercises</span><strong>${module.exercise_count || 0}</strong></div>
|
||||
<div class="mini-stat"><span>Ops</span><strong>${module.operation_count || 0}</strong></div>
|
||||
<div class="mini-stat"><span>Commands</span><strong>${module.command_count || 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();
|
||||
|
||||
Reference in New Issue
Block a user