feat(velxio): improve teacher arduino toolbar, add expected_wiring template, and harden deployer board detection
- Add EXPECTED_WIRING block to set-arduino template and standalone expected-wiring feature in teacher content editor - Reposition reload button to the far left inline with status dot, and shorten status text to 'Siap' / 'Menghubungkan...' - Decouple transfer from save: replace 'Simpan ke Dokumen' with 'Pindahkan Hasil' and centralize document saving to top header save button - Dynamically detect board FQBN from circuit in DeployTab with arduino:avr:uno fallback
This commit is contained in:
parent
5429dc17f9
commit
e4ab058078
|
|
@ -5,10 +5,12 @@
|
||||||
let {
|
let {
|
||||||
initialCode = '',
|
initialCode = '',
|
||||||
initialCircuit = '',
|
initialCircuit = '',
|
||||||
|
onTransfer,
|
||||||
onSave
|
onSave
|
||||||
}: {
|
}: {
|
||||||
initialCode?: string;
|
initialCode?: string;
|
||||||
initialCircuit?: string;
|
initialCircuit?: string;
|
||||||
|
onTransfer?: (state: { code: string; circuit: string }) => void;
|
||||||
onSave?: (state: { code: string; circuit: string }) => void;
|
onSave?: (state: { code: string; circuit: string }) => void;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
|
|
@ -63,17 +65,23 @@
|
||||||
showFeedback('Dimuat ulang dari dokumen');
|
showFeedback('Dimuat ulang dari dokumen');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleTransfer() {
|
||||||
const state = getCurrentState();
|
const state = getCurrentState();
|
||||||
if (!state) {
|
if (!state) {
|
||||||
showFeedback('Gagal membaca state simulator');
|
showFeedback('Gagal membaca state simulator');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onSave) {
|
if (onTransfer) {
|
||||||
|
onTransfer(state);
|
||||||
|
} else if (onSave) {
|
||||||
onSave(state);
|
onSave(state);
|
||||||
}
|
}
|
||||||
showFeedback('Tersimpan ke dokumen!');
|
showFeedback('Hasil dipindahkan ke editor');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
handleTransfer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFeedback(msg: string) {
|
function showFeedback(msg: string) {
|
||||||
|
|
@ -137,17 +145,7 @@
|
||||||
|
|
||||||
<div class="teacher-velxio-container">
|
<div class="teacher-velxio-container">
|
||||||
<div class="teacher-velxio-toolbar">
|
<div class="teacher-velxio-toolbar">
|
||||||
<div class="toolbar-status">
|
<div class="toolbar-left">
|
||||||
<span class="status-dot" class:ready={isReady}></span>
|
|
||||||
<span class="status-text">
|
|
||||||
{isReady ? 'Simulator Siap (Mode Lengkap)' : 'Menghubungkan Simulator...'}
|
|
||||||
</span>
|
|
||||||
{#if saveFeedback}
|
|
||||||
<span class="save-feedback">{saveFeedback}</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="toolbar-actions">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="toolbar-btn btn-reload"
|
class="toolbar-btn btn-reload"
|
||||||
|
|
@ -163,19 +161,33 @@
|
||||||
<span>Muat Ulang</span>
|
<span>Muat Ulang</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div class="toolbar-status">
|
||||||
|
<span class="status-dot" class:ready={isReady}></span>
|
||||||
|
<span class="status-text">
|
||||||
|
{isReady ? 'Siap' : 'Menghubungkan...'}
|
||||||
|
</span>
|
||||||
|
{#if saveFeedback}
|
||||||
|
<span class="save-feedback">{saveFeedback}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="toolbar-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="toolbar-btn btn-save"
|
class="toolbar-btn btn-transfer"
|
||||||
onclick={handleSave}
|
onclick={handleTransfer}
|
||||||
title="Simpan rangkaian dan kode saat ini langsung ke dokumen markdown"
|
title="Pindahkan rangkaian dan kode dari simulator ke dokumen editor"
|
||||||
disabled={!isReady}
|
disabled={!isReady}
|
||||||
>
|
>
|
||||||
<svg class="icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||||
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
<polyline points="14 2 14 8 20 8"></polyline>
|
||||||
<polyline points="7 3 7 8 15 8"></polyline>
|
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||||
|
<polyline points="10 9 9 9 8 9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Simpan ke Dokumen</span>
|
<span>Pindahkan Hasil</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -215,6 +227,13 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar-status {
|
.toolbar-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -292,13 +311,13 @@
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-save {
|
.btn-transfer {
|
||||||
background: var(--color-primary, #0d6efd);
|
background: var(--color-primary, #0d6efd);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-color: var(--color-primary, #0d6efd);
|
border-color: var(--color-primary, #0d6efd);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-save:hover:not(:disabled) {
|
.btn-transfer:hover:not(:disabled) {
|
||||||
background: var(--color-primary-dark, #0a58ca);
|
background: var(--color-primary-dark, #0a58ca);
|
||||||
border-color: var(--color-primary-dark, #0a58ca);
|
border-color: var(--color-primary-dark, #0a58ca);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,12 +163,21 @@
|
||||||
deployMessage = 'Mengompilasi kode...';
|
deployMessage = 'Mengompilasi kode...';
|
||||||
|
|
||||||
let code = '';
|
let code = '';
|
||||||
|
let boardFqbn = 'arduino:avr:uno';
|
||||||
|
|
||||||
if (velxioIframe) {
|
if (velxioIframe) {
|
||||||
const state = getVelxioState(velxioIframe);
|
const state = getVelxioState(velxioIframe);
|
||||||
if (state?.code) {
|
if (state?.code) {
|
||||||
code = state.code;
|
code = state.code;
|
||||||
}
|
}
|
||||||
|
if (state?.circuit) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(state.circuit);
|
||||||
|
if (parsed?.board) {
|
||||||
|
boardFqbn = parsed.board;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!code) {
|
if (!code) {
|
||||||
|
|
@ -179,7 +188,7 @@
|
||||||
throw new Error('Tidak ada kode Arduino untuk di-deploy');
|
throw new Error('Tidak ada kode Arduino untuk di-deploy');
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await getHexContent({ code, board_fqbn: 'arduino:avr:uno' });
|
const res = await getHexContent({ code, board_fqbn: boardFqbn });
|
||||||
|
|
||||||
if (!res.success || !res.hex_content) {
|
if (!res.success || !res.hex_content) {
|
||||||
throw new Error(res.error || 'Kompilasi gagal');
|
throw new Error(res.error || 'Kompilasi gagal');
|
||||||
|
|
|
||||||
|
|
@ -209,9 +209,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSaveVelxioFromTab(state: { code: string; circuit: string }) {
|
function handleTransferVelxioFromTab(state: { code: string; circuit: string }) {
|
||||||
applyVelxioToBody(state.code, state.circuit);
|
applyVelxioToBody(state.code, state.circuit);
|
||||||
await mgr.handleSave();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSaveAction() {
|
async function handleSaveAction() {
|
||||||
|
|
@ -375,10 +374,40 @@
|
||||||
'\n---EXPECTED_SERIAL_OUTPUT---\n' +
|
'\n---EXPECTED_SERIAL_OUTPUT---\n' +
|
||||||
'LED ON\nLED OFF\n' +
|
'LED ON\nLED OFF\n' +
|
||||||
'---END_EXPECTED_SERIAL_OUTPUT---\n' +
|
'---END_EXPECTED_SERIAL_OUTPUT---\n' +
|
||||||
|
'\n---EXPECTED_WIRING---\n' +
|
||||||
|
'{\n' +
|
||||||
|
' "wires": [\n' +
|
||||||
|
' {\n' +
|
||||||
|
' "start": { "componentId": "arduino-uno", "pinName": "13" },\n' +
|
||||||
|
' "end": { "componentId": "led-builtin", "pinName": "A" }\n' +
|
||||||
|
' },\n' +
|
||||||
|
' {\n' +
|
||||||
|
' "start": { "componentId": "arduino-uno", "pinName": "GND" },\n' +
|
||||||
|
' "end": { "componentId": "led-builtin", "pinName": "C" }\n' +
|
||||||
|
' }\n' +
|
||||||
|
' ]\n' +
|
||||||
|
'}\n' +
|
||||||
|
'---END_EXPECTED_WIRING---\n' +
|
||||||
'\n---KEY_TEXT---\n' +
|
'\n---KEY_TEXT---\n' +
|
||||||
'kata_kunci\n' +
|
'kata_kunci\n' +
|
||||||
'---END_KEY_TEXT---\n',
|
'---END_KEY_TEXT---\n',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'expected-wiring',
|
||||||
|
label: 'Expected Wiring',
|
||||||
|
group: 'set_soal',
|
||||||
|
snippet:
|
||||||
|
'\n---EXPECTED_WIRING---\n' +
|
||||||
|
'{\n' +
|
||||||
|
' "wires": [\n' +
|
||||||
|
' {\n' +
|
||||||
|
' "start": { "componentId": "arduino-uno", "pinName": "13" },\n' +
|
||||||
|
' "end": { "componentId": "led-builtin", "pinName": "A" }\n' +
|
||||||
|
' }\n' +
|
||||||
|
' ]\n' +
|
||||||
|
'}\n' +
|
||||||
|
'---END_EXPECTED_WIRING---\n',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'set-flowchart',
|
id: 'set-flowchart',
|
||||||
label: 'Set Flowchart',
|
label: 'Set Flowchart',
|
||||||
|
|
@ -648,7 +677,7 @@
|
||||||
{#snippet saveActions()}
|
{#snippet saveActions()}
|
||||||
{#if mgr.activePath && !isAssetFile}
|
{#if mgr.activePath && !isAssetFile}
|
||||||
<button class="panel-btn toolbar-toggle" class:active={featureSidebarOpen} onclick={() => featureSidebarOpen = !featureSidebarOpen} title="Tampilkan/sembunyikan panel Fitur">🧩</button>
|
<button class="panel-btn toolbar-toggle" class:active={featureSidebarOpen} onclick={() => featureSidebarOpen = !featureSidebarOpen} title="Tampilkan/sembunyikan panel Fitur">🧩</button>
|
||||||
<button class="panel-btn save-btn" onclick={() => handleSaveAction()} disabled={mgr.saving || !mgr.dirty} title="Simpan">
|
<button class="panel-btn save-btn" onclick={() => handleSaveAction()} disabled={mgr.saving || (!mgr.dirty && mgr.activeTab !== 'velxio')} title="Simpan">
|
||||||
{mgr.saving ? '⏳' : '💾'}
|
{mgr.saving ? '⏳' : '💾'}
|
||||||
</button>
|
</button>
|
||||||
<button class="panel-btn publish-btn" onclick={() => handlePublishAction()} disabled={mgr.publishing || !mgr.draftId} title="Publish">
|
<button class="panel-btn publish-btn" onclick={() => handlePublishAction()} disabled={mgr.publishing || !mgr.draftId} title="Publish">
|
||||||
|
|
@ -870,7 +899,8 @@
|
||||||
bind:this={velxioEditorRef}
|
bind:this={velxioEditorRef}
|
||||||
initialCode={extractedCodes.velxio}
|
initialCode={extractedCodes.velxio}
|
||||||
initialCircuit={extractedCircuitVelxio}
|
initialCircuit={extractedCircuitVelxio}
|
||||||
onSave={handleSaveVelxioFromTab}
|
onTransfer={handleTransferVelxioFromTab}
|
||||||
|
onSave={handleTransferVelxioFromTab}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else if dynTab.id === 'flowchart'}
|
{:else if dynTab.id === 'flowchart'}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue