feat: add actions snippet to OutputPanel and implement run-all evaluation logic for lessons
parent
997ab78f56
commit
314975ac65
|
|
@ -18,9 +18,10 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
sections?: OutputEntry[];
|
sections?: OutputEntry[];
|
||||||
|
actions?: import('svelte').Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { sections = [] }: Props = $props();
|
let { sections = [], actions }: Props = $props();
|
||||||
|
|
||||||
let showDebug = $state<Record<string, boolean>>({});
|
let showDebug = $state<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
|
@ -36,6 +37,7 @@
|
||||||
|
|
||||||
<div class="output-panel">
|
<div class="output-panel">
|
||||||
<div class="output-header">
|
<div class="output-header">
|
||||||
|
<div class="header-info">
|
||||||
<span class="output-title">Output</span>
|
<span class="output-title">Output</span>
|
||||||
{#if anyLoading}
|
{#if anyLoading}
|
||||||
<span class="status-badge running">Compiling...</span>
|
<span class="status-badge running">Compiling...</span>
|
||||||
|
|
@ -45,6 +47,12 @@
|
||||||
<span class="status-badge error">Error</span>
|
<span class="status-badge error">Error</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{#if actions}
|
||||||
|
<div class="header-actions">
|
||||||
|
{@render actions()}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="output-sections">
|
<div class="output-sections">
|
||||||
{#each sections as sec (sec.key)}
|
{#each sections as sec (sec.key)}
|
||||||
|
|
@ -94,6 +102,16 @@
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, sans-serif;
|
||||||
}
|
}
|
||||||
|
.header-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
.output-title {
|
.output-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@
|
||||||
|
|
||||||
/** Mark lesson as complete: track progress + celebration. Called when ALL exercises pass. */
|
/** Mark lesson as complete: track progress + celebration. Called when ALL exercises pass. */
|
||||||
async function completeLesson() {
|
async function completeLesson() {
|
||||||
|
if (lessonCompleted) return;
|
||||||
showCelebration = true;
|
showCelebration = true;
|
||||||
if (auth.isLoggedIn) {
|
if (auth.isLoggedIn) {
|
||||||
const lessonName = slug.replace('.md', '');
|
const lessonName = slug.replace('.md', '');
|
||||||
|
|
@ -298,17 +299,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleRun() {
|
async function evaluateLanguage(lang: 'c' | 'python') {
|
||||||
if (activeTab === 'circuit') { await evaluateCircuit(); return; }
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
const out = getCodeOut();
|
const out = lang === 'c' ? cOut : pyOut;
|
||||||
Object.assign(out, { loading: true, output: '', error: '', success: null });
|
Object.assign(out, { loading: true, output: '', error: '', success: null });
|
||||||
activeTab = 'output';
|
activeTab = 'output';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const code = editor?.getCode() ?? currentCode;
|
const code = (currentLanguage === lang) ? (editor?.getCode() ?? currentCode) : (lang === 'c' ? cCode : pythonCode);
|
||||||
const res = await compileCode({ code, language: currentLanguage });
|
const res = await compileCode({ code, language: lang });
|
||||||
|
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
Object.assign(out, { error: res.error || 'Compilation failed', success: false });
|
Object.assign(out, { error: res.error || 'Compilation failed', success: false });
|
||||||
|
|
@ -319,13 +319,13 @@
|
||||||
out.success = true;
|
out.success = true;
|
||||||
|
|
||||||
if (data.expected_output) {
|
if (data.expected_output) {
|
||||||
const currentCCode = currentLanguage === 'c' ? code : cCode;
|
const currentCCode = (currentLanguage === 'c') ? code : cCode;
|
||||||
const currentPythonCode = currentLanguage === 'python' ? code : pythonCode;
|
const currentPythonCode = (currentLanguage === 'python') ? code : pythonCode;
|
||||||
const mergedCode = currentCCode + '\n' + currentPythonCode;
|
const mergedCode = currentCCode + '\n' + currentPythonCode;
|
||||||
const passed = res.output.trim() === data.expected_output.trim() && checkKeyText(mergedCode, data.key_text ?? '');
|
const passed = res.output.trim() === data.expected_output.trim() && checkKeyText(mergedCode, data.key_text ?? '');
|
||||||
if (passed) {
|
if (passed) {
|
||||||
if (currentLanguage === 'c') cPassed = true;
|
if (lang === 'c') cPassed = true;
|
||||||
else if (currentLanguage === 'python') pythonPassed = true;
|
else if (lang === 'python') pythonPassed = true;
|
||||||
|
|
||||||
if (!checkAllPassed()) {
|
if (!checkAllPassed()) {
|
||||||
out.output += '\n✅ Kode benar!\n⏳ Selesaikan juga tantangan di tab lainnya untuk menyelesaikan pelajaran ini.';
|
out.output += '\n✅ Kode benar!\n⏳ Selesaikan juga tantangan di tab lainnya untuk menyelesaikan pelajaran ini.';
|
||||||
|
|
@ -352,6 +352,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleRun() {
|
||||||
|
if (activeTab === 'circuit') { await evaluateCircuit(); return; }
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
activeTab = 'output';
|
||||||
|
await evaluateLanguage(currentLanguage as 'c' | 'python');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRunAll() {
|
||||||
|
if (!data) return;
|
||||||
|
activeTab = 'output';
|
||||||
|
|
||||||
|
const tabs = data.active_tabs ?? [];
|
||||||
|
const hasC = tabs.includes('c') || (!tabs.length && !tabs.includes('python'));
|
||||||
|
const hasPython = tabs.includes('python');
|
||||||
|
|
||||||
|
if (hasC) await evaluateLanguage('c');
|
||||||
|
if (hasPython) await evaluateLanguage('python');
|
||||||
|
if (tabs.includes('circuit')) await evaluateCircuit();
|
||||||
|
if (tabs.includes('velxio')) await handleVelxioSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
function handleReset() {
|
function handleReset() {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (activeTab === 'circuit') {
|
if (activeTab === 'circuit') {
|
||||||
|
|
@ -922,7 +944,13 @@
|
||||||
|
|
||||||
<!-- Output tab panel -->
|
<!-- Output tab panel -->
|
||||||
<div class="tab-panel" class:tab-hidden={activeTab !== 'output'}>
|
<div class="tab-panel" class:tab-hidden={activeTab !== 'output'}>
|
||||||
<OutputPanel sections={outputSections} />
|
<OutputPanel sections={outputSections}>
|
||||||
|
{#snippet actions()}
|
||||||
|
<button class="btn btn-success btn-sm btn-run-all" onclick={handleRunAll} disabled={compiling}>
|
||||||
|
{compiling ? 'Mengevaluasi...' : '▶ Run Keseluruhan'}
|
||||||
|
</button>
|
||||||
|
{/snippet}
|
||||||
|
</OutputPanel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -1039,6 +1067,10 @@
|
||||||
.btn-secondary:hover {
|
.btn-secondary:hover {
|
||||||
background: var(--color-border);
|
background: var(--color-border);
|
||||||
}
|
}
|
||||||
|
.btn-run-all {
|
||||||
|
padding: 0.3rem 0.6rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
.lang-label {
|
.lang-label {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue