Commit Graph

77 Commits

Author SHA1 Message Date
a2nr 7e17da46c2 fix(frontend): hide CircuitJS toolbar & menu bar in lesson embeds
Append &hideMenu=true to the circuitjs.html iframe src used by
renderCircuitEmbeds so lesson content embeds render clean without
the toolbar and menu bar. The embed path in CircuitEditor.svelte
is untouched, so the editor keeps its full toolbar.

Verified:
- Bundle contains embed URL (with hideMenu=true) and editor URL
  (without) as separate variants
- svelte-check: 0 errors
- /lesson/rangkaian_dasar returns 200 on the running stack
2026-08-12 07:49:34 +07:00
a2nr 24502380aa feat(quiz): strict focus-loss anti-cheat + audit attempt
Kebijakan strict: kehilangan fokus saat kuis aktif langsung mengakhiri kuis
dengan penalti, satu attempt per kuis tercatat audit, pembahasan disembunyikan
saat focus_lost, badge pelanggaran muncul di laporan guru.

Backend:
- tabel quiz_attempts (migration 0002) + endpoint POST /quiz-attempts/submit
  idempotent dengan unique constraint (satu attempt per token+lesson, 409
  untuk duplikat), bounded termination_reason
- models/repositories/storage: QuizAttempt + record/query pelanggaran untuk
  laporan guru (progress)

Frontend:
- quiz-integrity.ts: policy hub (isFocusLossEvent, terminateQuiz,
  shouldShowQuizReview) + attempt_id via crypto.randomUUID()
- lesson.svelte.ts: finalisasi idempotent; sendBeacon untuk exit path yang
  bisa suspend (focus_lost/page_unload), fetch keepalive untuk reason lain
- QuizTab.svelte: summary menyembunyikan pembahasan saat focus_lost
- progress/+page.svelte: badge pelanggaran focus_lost di laporan guru
- APP_VERSION 26 -> 27

Tests & docs:
- 204 backend test pass (unit host + integrasi container, DB elemes_test)
- 107 frontend test pass, svelte-check 0 error
- docs/12-quiz-integrity.md (baru), docs/07-quiz-authoring.md + README FAQ
  anti-cheat diperbarui
2026-08-11 20:55:39 +07:00
a2nr c356e234fa fix(frontend): resolve 26 svelte-check type errors
- add @types/node for hooks.server process/env types
- fix katex auto-render import path (contrib/auto-render)
- declare global Web Bluetooth API types in ble-deployer
- guard simApi null in CircuitEditor autosave interval
- default-type imports for CodeEditor/CircuitEditor
- make lesson slug getter non-nullable
- add expected_flowchart to LessonContent type
- key lesson workspace by slug; drop phantom onReset prop
- guard CodeTab render when lesson data is null
2026-08-09 12:28:17 +07:00
a2nr d602b0f642 feat(student-management): kelola siswa round-trip CSV + bulk delete (PG)
- Export siswa terpilih/seluruh + progress ke CSV round-trip (token tidak
  pernah diekspor; teacher tidak ikut; urutan deterministik)
- Import preview all-or-nothing: siswa existing di-restore (nama+progress,
  token lama dipertahankan), siswa baru dibuat dari student_id kosong +
  token; conflict tidak menyebut raw token/digest
- Bulk delete multi-seleksi dengan validasi ketat (teacher terlindungi,
  zero-delete bila ada ID bermasalah)
- fix(import): deteksi delimiter berbasis schema — menerima titik koma
  (;) maupun koma (,), ekspor tetap ';' + BOM; header tab/campuran ditolak
  dengan pesan jelas (regresi: file Excel/Sheets kini bisa diimpor)
- refactor: progress_status.py — kontrak status legacy tunggal dipakai
  storage PG, export, dan import; report siswa hanya role student
- UI /progress: dialog import/export + bulk delete, selection store,
  respon import tanpa raw token; APP_VERSION 22 -> 26
- ops: ORIGIN env var di podman-compose.yml
- docs: README section 9 (workflow round-trip + delimiter) dan
  docs/11-database-migration (kelola siswa via round-trip CSV)
- test: parser round-trip (45), repositories, route management (24,
  PostgreSQL), frontend 91, fixtures CSV valid/invalid
2026-08-09 07:29:19 +07:00
a2nr ac37db5c91 feat(storage): migrasi penyimpanan CSV ke PostgreSQL (SQLAlchemy + Alembic)
- facade storage: csv_backend + postgres_backend, dipilih via STORAGE_BACKEND
- token disimpan sebagai HMAC-SHA256 digest (TOKEN_PEPPER), tanpa plaintext
- schema alembic 0001: users, access_tokens, lessons, student_progress
- importer CSV idempotent + dry-run + sparse model (not_started tanpa row)
- lesson registry: sync dari home.md (lesson hilang di-deactivate, bukan dihapus)
- routes progress/auth: report & reset via DB, validasi via token hash
- elemes.sh: dbupgrade/dbstatus/dbimport/synclessons/dbverify/dbbackup/dbrestore/dbexport
- podman-compose: service postgres (PG18), tokens_siswa.csv mount :ro
- frontend: api client + progress page; tests kontrak dual-backend + isolasi DB
- load-test: skenario database (locustfile_db.py)
- docs: 01-11 termasuk 11-database-migration.md (arsitektur & operasional)
2026-08-08 04:57:08 +00:00
a2nr ec66d794a8 feat(playground): alur interaktif Run→prompt→jawab + file tree collapsible & dukungan .h
- store: normalisasi lifecycle sesi (runStatus/runSessionId/outputCursor/runError,
  stdinQueue tetap untuk kompatibilitas)
- +page.svelte: session controller — start, polling recursive setTimeout non-overlap,
  delta output via cursor, stop + cleanup unmount/generation counter
- ConsolePanel.svelte: input interaktif (onSendInput/onStop async, mode antrean vs kirim)
- FileTree.svelte: validasi rename (foo.h diterima di C), tombol + langsung mode rename,
  collapsible shell dengan handle persisten
- playground-files.ts: validasi nama file (basename, traversal, duplikat case-insensitive)
- api.ts/compiler.ts: client sesi interaktif
- tests: playground.test.ts + playground-files.test.ts
2026-08-02 14:14:53 +00:00
a2nr 1aef334d59 feat(playground): stdinQueue di store — enqueue/consume/clear atomik 2026-08-01 22:34:32 +00:00
a2nr dbed014474 feat(playground): multi-tab playground + FileTree + Console + stdin wiring to compile worker
- playground route: velxio/flowchart/circuit/code tabs with auto-save
- FileTree + ConsolePanel for C/Python code tab
- compileCode() forwards stdin through routes/compile.py to compiler_worker
- compiler_worker run_c_code/run_python_code accept stdin, pass via subprocess.run(input=)
- known issue: Console UI does not queue stdin properly → fix in next cycle
2026-08-01 22:32:54 +00:00
a2nr 02d5969619 refactor(quiz): restructure quiz session, question view, and tab safety
Restructure quiz flow into QuizSession primitive + atomic quiz state in
LessonManager, and extract QuizQuestionView for split-view display.

Tab-safety fix: add lesson-tabs.ts with pickDefaultTab(), getAvailableTabs()
mirroring the exact {#if} conditions in +page.svelte, and ensureActiveTab()
safety-net that falls back to pickDefaultTab() when the desired tab is not
rendered. A reactive $effect in lesson/[slug]/+page.svelte validates
activeTab against lesson data on every change, preventing the "tab quiz
kosong" (empty workspace) regression for quiz-only lessons and phantom-tab
state after data changes.

Files:
- lib/services/lesson-tabs.ts (+test): pure helpers, 18 tests pass
- lesson/[slug]/+page.svelte: ensureActiveTab reactive guard + QuizQuestionView
- lesson/[slug]/lesson.svelte.ts: atomic startQuiz, getExitStatus, idempotent
  submit on navigate/exit
- lib/services/quiz-session.ts (+test), lib/types/quiz.ts, QuizTab.svelte
  (mgr-driven), QuizQuestionView.svelte, lesson.css
- services/lesson_service.py, services/tests/test_lesson_service_quiz.py
  (test_lesson_service_quiz.py), requirements.txt (+pytest)
- docs/07-quiz-authoring.md
- version.ts APP_VERSION 21 -> 22 (forces SW update for the fix)
- examples/content/dasar/quiz_test.md synced to live content root
2026-08-01 00:14:29 +07:00
a2nr 1b2c51f72f chore: bump version to 19 for playground multi-tab 2026-07-24 13:46:18 +07:00
a2nr 98adc0b860 fix(usb-deployer): review fixes — reset pumpRunning on exit, clear buffer on readExact timeout
WARN-1: pumpRunning now reset to false when the pump loop exits via
{done} or error, so future startPump() isn't a silent no-op.
WARN-3: readExact clears rxBuffer on timeout to avoid stale bytes
leaking into the next non-retried STK500 command.
2026-07-19 19:15:20 +07:00
a2nr 6f2f9c589a chore(frontend): bump APP_VERSION to 18 for SW cache-bust
Service worker evicts old caches on activate, so users always
get the new usb-deployer bundle with the read-pump fix instead
of a stale cached one.
2026-07-19 19:09:45 +07:00
a2nr 056b1a47b6 refactor(usb-deployer): serial monitor + flashBuffer + lifecycle use pump
- startSerialMonitor/setBaudRate stop+restart pump, subscribe rxBuffer.onData
- flashBuffer stops pump, reuses/reopens port, starts pump, drains before sync
- stopReaderWriter now just releases locks; cleanup stops pump first
- remove dead keepReading field
2026-07-19 19:09:10 +07:00
a2nr f7c088013a fix(usb-deployer): read from rxBuffer, kill orphaned-read race in get_sync
readExact and drainStray now consume the FIFO buffer fed by the pump
instead of calling reader.read() directly. No orphaned reader.read()
promise can swallow optiboot INSYNC/OK replies anymore. This is the
core fix for "get_sync failed after N attempts".
2026-07-19 19:06:44 +07:00
a2nr 121d592ce0 feat(usb-deployer): add read pump + start pump after pair
Single pump loop becomes the only reader.read() caller, feeding
rxBuffer. Serial monitor + readExact/drain will consume from the
buffer (next tasks). Build passes.
2026-07-19 19:05:54 +07:00
a2nr bf4b1a4932 feat(frontend): add vitest + ByteStreamBuffer FIFO for orphan-read fix
Test runner setup (vitest) plus deadline-based FIFO buffer that
becomes the single sink for serial bytes. readExact keeps
partial bytes buffered on timeout — the exact case that dropped
optiboot INSYNC/OK replies under the orphaned-reader race.
7 unit tests pass.
2026-07-19 19:03:40 +07:00
a2nr f164134222 fix(usb-deployer): CH340 port reuse fix — skip reopen, single DTR, baud tracking 2026-07-17 14:35:30 +07:00
a2nr 2773bf2a83 feat(usb-deployer): double DTR reset + post-write delay for stubborn clones 2026-07-17 12:47:58 +07:00
a2nr 95a3569be9 chore: bump version to v17 2026-07-17 11:07:46 +07:00
a2nr 8adc28b745 refactor(usb-deployer): use named settle constant and fix comment 2026-07-17 10:38:40 +07:00
a2nr 852fccacdf fix(usb-deployer): rebalance sync retries/backoff for optiboot window 2026-07-17 10:37:57 +07:00
a2nr 84827350af fix(usb-deployer): drain stray bytes until quiet window 2026-07-17 10:37:08 +07:00
a2nr 3d941191e3 refactor(usb-deployer): name timing constants for sync window tuning 2026-07-17 10:35:23 +07:00
a2nr 4ec5d4cb92 fix(usb-deployer): add detailed diagnostic logging for sync failures 2026-07-17 10:24:39 +07:00
a2nr 7dd8e5d522 fix(usb-deployer): preliminary get_sync robustness improvements 2026-07-17 10:23:39 +07:00
a2nr 24db01fd8d refactor: Footer.svelte imports APP_VERSION from $lib/version 2026-07-17 09:46:29 +07:00
a2nr 12725cbdad chore: bump version to v16 2026-07-17 09:26:17 +07:00
a2nr 369897529d fix: harden USBHardwareDeployer — drain after timeout, async cleanup, flash error recovery, baud切换 2026-07-17 08:15:21 +07:00
a2nr f8e92875c9 feat: add USBHardwareDeployer with STK500v1 protocol via Web Serial API 2026-07-17 08:10:12 +07:00
a2nr 1b51e75ff0 feat: add HardwareDeployer interface, BLEHardwareDeployer implements it 2026-07-17 08:03:42 +07:00
a2nr 87364da0c5 feat(deploy-fab): auto-reset Arduino when starting serial monitor 2026-07-05 17:48:44 +07:00
a2nr c6c6daecbf feat(ble-deployer): add resetArduino() method for DTR pulse 2026-07-05 17:46:32 +07:00
a2nr a18d3f660c feat(ble-deployer): add CMD_RESET for Arduino DTR pulse reset 2026-07-05 17:38:40 +07:00
a2nr bba06933bd feat(elemes): v3.1 checkpoint — BLE deployer enhancements before port to Velxio
v3.1 enhancements (4 Jul 2026):
- Baud dropdown (9600/19200/38400) + CMD_SET_BAUD
- LED blink patterns per-state (IDLE/RECEIVING/FLASHING/SERIAL_BRIDGE)
- Arduino event-based connect/disconnect (CherryUSB)
- Serial terminal always visible saat paired
- Re-deploy fix: serial_bridge_stop before parser
- Dual baud: 115200 STK500 + 9600 serial bridge
- Footer v15, SW cache v14

This is a checkpoint commit. Next cycle: port Deploy from
SvelteKit (DeployTab.svelte) to Velxio React app (native integration).
DeployTab.svelte will be deprecated after port complete.
2026-07-04 20:21:46 +07:00
a2nr 3dd29e1ba1 revert: DATA sendCommandWithRetry ke f6a04b8 (direct write+waitForAck, no readPoll)
cad90eb merutekan DATA melalui sendCommand() dengan readPoll state===1
yang selalu true — menyebabkan chunk lambat 5s/kirim saat ACK di-drop.
Kembalikan ke direct writeValueWithResponse + waitForAck.
Selama transfer aktif tidak ada idle gap (Qualcomm >8s), jadi ACK notify
tidak terpengaruh idle-drop.

Perubahan:
- sendCommandWithRetry: build payload sendiri, pakai chunkCRC param,
  write+waitForAck langsung (tidak lewat sendCommand)
- sendCommand: hanya handle INIT dan END (DATA dihapus dari blok)
- Hapus writeSucceeded flag (tidak lagi dipakai)
- Hapus readPoll fallback untuk DATA
- Bump v12 (Footer + SW cache)
2026-07-01 06:07:57 +07:00
a2nr 26ada16c4d feat(frontend): bump version badge + SW cache to v11 for deploy verification 2026-07-01 05:12:42 +07:00
a2nr c20b2d5a8e fix: END ACK regression — readPoll pintar + INIT_ACK_INDEX 0xFFFE + binary_parser.c git track
Akar masalah:
- 4a6ef8c menambah binary_parser_reset() di handler re-deploy SERIAL_BRIDGE
  yang menghapus expected_total_crc setelah INIT — menyebabkan CRC mismatch
  di END. Source 1d3a489 sudah menghapus reset-nya, tapi device butuh rebuild.
- cad90eb merutekan DATA melalui sendCommand() dengan readPoll state===1
  yang selalu true saat DATA — write gagal dilaporkan sukses.

Perbaikan:
1. readPoll pintar untuk DATA: state===1 tidak lagi dianggap sukses.
   readPoll hanya fast-fail di state>=5; deadline fallback hanya ok
   jika writeValueWithResponse sukses (GATT write response = bukti delivery).
2. INIT_ACK_INDEX=0xFFFE (Fase 3): inisialisasi INIT dengan index unik
   menghilangkan tabrakan dengan DATA[0] (index 0). binary_parser.c echo
   index recv di ACK INIT. Guard handleFlashingNotification diperluas.
3. pendingAcks.clear() di awal deployHex mencegah poisoning antar deploy.
4. Track firmware inti ke git: binary_parser.c, checksum.c, stk500v1.c,
   usb_host.c + headers + CMakeLists + sdkconfig + partitions.

Build: ESP-IDF v6, ESP32-S3, flash via /dev/ttyACM0
2026-07-01 05:10:38 +07:00
a2nr d146e445d6 feat(frontend): add version badge (v10) in footer for deploy verification 2026-06-30 19:39:15 +07:00
a2nr 1d3a48940e fix(firmware+frontend): remove binary_parser_reset() re-deploy wipes CRC + tighten INIT poll
Firmware (state_machine.c):
- Remove binary_parser_reset() from SERIAL_BRIDGE→RECEIVING handler
- Remove binary_parser_reset() from ERROR→RECEIVING handler
- Parser's INIT handler already resets itself; calling reset AFTER
  INIT payload processing wipes expected_total_crc to 0 → CRC fail

Frontend (ble-deployer.ts):
- Tighten INIT readValue poll: state===1 (RECEIVING) only
  Previously accepted state >= 1 && <= 4 which could false-positive
  on FLASHING(3) or SERIAL_BRIDGE(4) stale states

Cache v9 → v10
2026-06-30 19:21:35 +07:00
a2nr cad90ebd2e fix(frontend): route DATA through sendCommand for readValue() fallback
sendCommandWithRetry now delegates to sendCommand() instead of
using raw writeValueWithResponse + await ackPromise. This ensures
DATA chunks also benefit from the readValue() state poll fallback
when Android BLE notify is unreliable after idle.
2026-06-30 18:14:03 +07:00
a2nr f6a04b81ba fix: re-deploy crash + readValue poll for INIT/DATA
Firmware (state_machine.c):
- Add EVENT_BLE_INIT handler to SERIAL_BRIDGE state
  -> transition back to RECEIVING for re-deploy
- Add EVENT_BLE_INIT handler to ERROR states
  -> allows re-deploy from error without pressing Retry

Frontend (ble-deployer.ts):
- Add readValue() state poll for INIT and DATA commands
  (same Android notify idle-drop bug affects these too)
- INIT success: firmware state >= 1 (RECEIVING+)
- DATA success: firmware state == 1 (RECEIVING)
- Error on state >= 5
2026-06-30 17:55:26 +07:00
a2nr 9918aafe41 fix: Phase B — readValue() state poll fallback for END ACK
Firmware:
- Add BLE_GATT_CHR_F_READ to flashing characteristic
- Read callback returns state_machine_get_current() as 1 byte
  (4=SERIAL_BRIDGE=success, 5/6=ERROR)

Frontend:
- Replace ineffective pendingAcks poll with readValue() poll
- CCCD refresh fire-and-forget (non-blocking)
- Race ackPromise (notify) vs readPoll (readValue)
- Clean up ackPromise resolver on readPoll win
- Cache v7 → v8
2026-06-30 17:35:23 +07:00
a2nr 9dda8fe3e6 fix: event-driven poll loop for END ACK to work around Android BLE notification delivery issue on Qualcomm chipsets
- Race ackPromise against a polling loop that yields to the event loop
  every 200ms (vs a single blocking await that can miss notifications)
- bump SW cache v5→v6
2026-06-30 13:12:55 +07:00
a2nr e4ec3f1ded feat: custom evaluation config 2026-06-17 20:34:36 +07:00
a2nr 120a1338c0 feat: enhance fullscreen mode slide and hide able slide 2026-05-14 20:25:21 +07:00
a2nr 58e897f743 feat: add markdown-based interactive slide carousel with fullscreen mode 2026-05-14 18:53:46 +07:00
a2nr 097f2c5609 feat: enhance lesson UI with centered images, bordered tables, and conditional output tab 2026-05-11 07:27:15 +07:00
a2nr 459ceff2d8 fix: prevent localStorage leakage and fix navigation by implementing component isolation and reactive state management 2026-05-10 09:14:00 +07:00
a2nr ea3556d05f feat: implement interactive quiz with secure content protection, scoring, and teacher reset 2026-05-09 13:04:42 +07:00
a2nr 7e77748b6b feat: add quiz feature 2026-05-08 09:50:07 +07:00