# Proposal Refactoring: LMS Belajar Pemrograman C **Tanggal:** 24 Maret 2026 **Project:** LMS-C (Learning Management System untuk Pemrograman C) **Workspace:** `elemes/` --- ## 1. Ringkasan Eksekutif Proposal ini mengusulkan refactoring LMS-C dari arsitektur **Flask monolith + Jinja2 templates** menjadi arsitektur **Flask API + SvelteKit PWA**. Tujuannya adalah memberikan pengalaman belajar yang optimal di **desktop dan mobile** dengan code editor yang proper, performa cepat, dan kemampuan instalasi sebagai aplikasi (PWA). --- ## 2. Kondisi Saat Ini ### Arsitektur - **Backend:** Flask monolith (`app.py`, 982 baris) — semua logic dalam satu file - **Frontend:** Jinja2 templates + vanilla JavaScript + Bootstrap 5 - **Code Editor:** Plain textarea dengan line numbers manual (bukan code editor sesungguhnya) - **Compiler:** Factory pattern — CCompiler (gcc) dan PythonCompiler - **Auth:** Token-based via CSV file (`tokens_siswa.csv`) - **Deploy:** Podman container + Tailscale sidecar untuk HTTPS - **Flutter app:** 0% — hanya folder kosong, tidak ada kode sama sekali ### Masalah Utama 1. **Monolith 982 baris** — sulit di-maintain dan di-test 2. **Bukan code editor** — textarea biasa, tidak ada syntax highlighting saat mengetik, bracket matching, atau auto-indent 3. **Tidak mobile-friendly** — code editor tidak bisa dipakai di mobile 4. **Tidak bisa offline** — tidak ada caching mekanisme 5. **Vanilla JS** — duplikasi kode di setiap template (login logic di-copy paste) 6. **Tidak installable** — harus buka browser setiap kali --- ## 3. Evaluasi Framework ### 3.1 Flutter Web + Mobile | Aspek | Evaluasi | |-------|----------| | **Rendering** | CanvasKit — render ke HTML canvas, BUKAN native DOM | | **Code Editor** | Tidak ada widget setara CodeMirror/Monaco. `code_text_field` sangat terbatas | | **Bundle Size** | 2-4 MB initial download (CanvasKit engine) | | **Mobile** | Excellent untuk native app, tapi web version bermasalah | | **Accessibility** | Buruk — canvas rendering, tidak bisa "Find in Page", screen reader tidak jalan | | **SEO** | Tidak ada — semua di-render di canvas | | **Markdown** | Ecosystem terbatas untuk rendering markdown | **Verdict:** ❌ Tidak cocok untuk LMS yang content-heavy dengan code editor sebagai fitur utama. **Alasan utama penolakan:** Flutter Web me-render SELURUH UI ke HTML ``. Ini berarti: - Siswa tidak bisa copy-paste text dari lesson content secara natural - Browser "Find in Page" (Ctrl+F) tidak berfungsi - Code editor di Flutter tidak bisa match fitur CodeMirror 6 (syntax highlighting, bracket matching, multiple cursors, mobile keyboard support) - Bundle 2-4 MB sangat berat untuk siswa dengan koneksi mobile ### 3.2 Next.js (React) PWA | Aspek | Evaluasi | |-------|----------| | **Rendering** | Native DOM + SSR/SSG | | **Code Editor** | Monaco Editor (VS Code engine) — sangat powerful tapi 5MB+ | | **Bundle Size** | 200-400 KB base, 5MB+ dengan Monaco | | **Mobile** | Baik, tapi Monaco Editor tidak optimal di mobile | | **Ecosystem** | Sangat besar, banyak library | | **Complexity** | Lebih kompleks dari Svelte (virtual DOM, hooks, etc.) | **Verdict:** ⚠️ Bisa dipakai, tapi overkill dan Monaco terlalu berat. ### 3.3 SvelteKit PWA (REKOMENDASI) | Aspek | Evaluasi | |-------|----------| | **Rendering** | Native DOM + SSR, compile-time (no virtual DOM) | | **Code Editor** | CodeMirror 6 — production-grade, mobile-friendly, 150KB | | **Bundle Size** | 50-100 KB base + 150KB CodeMirror = ~250KB total | | **Mobile** | Excellent — native HTML/CSS responsive, CodeMirror 6 support touch | | **PWA** | Built-in via `@vite-pwa/sveltekit` | | **SSR** | Built-in — fast first paint untuk konten markdown | | **Learning Curve** | Minimal — Svelte lebih sederhana dari React | | **TypeScript** | Native support | **Verdict:** ✅ Pilihan terbaik untuk project ini. ### 3.4 Flask + HTMX/Alpine.js | Aspek | Evaluasi | |-------|----------| | **Perubahan** | Minimal dari arsitektur sekarang | | **Code Editor** | Bisa pakai CodeMirror, tapi integrasi manual | | **Mobile** | Terbatas — tetap server-rendered, tidak ada PWA | | **Offline** | Tidak ada | **Verdict:** ⚠️ Perbaikan inkremental, bukan refactor sebenarnya. ### Tabel Perbandingan | Kriteria | Flutter Web | Next.js | **SvelteKit** | Flask+HTMX | |----------|:-----------:|:-------:|:-------------:|:----------:| | Code editor quality | 2/10 | 9/10 | **9/10** | 5/10 | | Bundle size | 2/10 | 5/10 | **9/10** | 10/10 | | Mobile experience | 4/10 | 7/10 | **9/10** | 4/10 | | PWA support | 3/10 | 7/10 | **9/10** | 2/10 | | SSR / first paint | 2/10 | 9/10 | **9/10** | 8/10 | | Complexity | 6/10 | 5/10 | **8/10** | 9/10 | | Markdown rendering | 4/10 | 8/10 | **9/10** | 8/10 | | **Total** | **23/70** | **50/70** | **62/70** | **46/70** | --- ## 4. Arsitektur yang Diusulkan ### 4.1 Overview ``` ┌─────────────────────────────┐ │ Internet (HTTPS) │ └──────────────┬──────────────┘ │ ┌──────────────▼──────────────┐ │ Tailscale Funnel (:443) │ │ (elemes-ts container) │ └──────────────┬──────────────┘ │ ┌──────────────▼──────────────┐ │ SvelteKit Frontend (:3000) │ │ - SSR pages │ │ - CodeMirror 6 editor │ │ - PWA + Service Worker │ │ - Responsive UI │ └──────────────┬──────────────┘ │ /api/* ┌──────────────▼──────────────┐ │ Flask API Backend (:5000) │ │ - Code compilation (gcc) │ │ - Token authentication │ │ - Progress tracking (CSV) │ │ - Lesson content parsing │ └─────────────────────────────┘ ``` ### 4.2 Tiga Container | Container | Base Image | Port | Fungsi | |-----------|-----------|------|--------| | `elemes-frontend` | Node 20 slim | 3000 | SvelteKit SSR, static assets, PWA | | `elemes-api` | Python 3.11 slim + gcc | 5000 | Flask JSON API, compilation, auth | | `elemes-ts` | Tailscale | 443 | HTTPS termination, Funnel | ### 4.3 Alur Data ``` Siswa buka browser → GET /lesson/hello_world → SvelteKit SSR: fetch /api/lesson/hello_world dari Flask → Flask: parse markdown, return JSON {lesson_html, initial_code, ...} → SvelteKit: render HTML + hydrate CodeMirror editor → Siswa tulis kode C di CodeMirror → Klik "Run" → POST /api/compile {code: "..."} → Flask: write temp file → gcc compile → run → return output → SvelteKit: tampilkan output di OutputPanel → Jika benar → POST /api/track-progress → Flask: update CSV file ``` --- ## 5. Struktur Direktori ``` lms-c/ # Root — konten saja ├── content/ # 25 lesson markdown │ ├── home.md │ ├── hello_world.md │ ├── variables.md │ └── ... (25 file) ├── assets/ # Gambar untuk lesson ├── tokens_siswa.csv # Data siswa & progress ├── config/ │ └── sinau-c-tail.json # Tailscale config ├── state/ # Runtime state (certs, logs) └── .env # Environment variables elemes/ # Semua kode aplikasi ├── documentation.md # Dokumen ini │ ├── api/ # ── Flask API Backend ── │ ├── app.py # create_app() factory │ ├── config.py # Environment config │ ├── requirements.txt # Python dependencies │ ├── Dockerfile │ ├── gunicorn.conf.py │ ├── compiler/ # Compiler module (tidak berubah) │ │ ├── __init__.py # CompilerFactory │ │ ├── base_compiler.py # Abstract base │ │ ├── c_compiler.py # gcc wrapper │ │ └── python_compiler.py # python wrapper │ ├── routes/ # Flask Blueprints (JSON only) │ │ ├── auth.py # /api/login, logout, validate-token │ │ ├── compile.py # /api/compile │ │ ├── lessons.py # /api/lessons, /api/lesson/ │ │ └── progress.py # /api/track-progress, progress-report │ ├── services/ # Business logic │ │ ├── token_service.py # CSV token operations │ │ └── lesson_service.py # Markdown parsing │ └── tests/ # pytest │ ├── test_auth.py │ ├── test_compile.py │ ├── test_lessons.py │ └── test_progress.py │ ├── frontend/ # ── SvelteKit PWA Frontend ── │ ├── package.json │ ├── svelte.config.js # adapter-node │ ├── vite.config.ts # PWA plugin + API proxy │ ├── Dockerfile │ └── src/ │ ├── app.html # Root HTML shell │ ├── app.css # Global styles │ ├── lib/ │ │ ├── components/ │ │ │ ├── CodeEditor.svelte # CodeMirror 6 (KRITIS) │ │ │ ├── Navbar.svelte # Navigation + auth │ │ │ ├── LessonCard.svelte # Card lesson │ │ │ ├── OutputPanel.svelte # Output kompilasi │ │ │ ├── ProgressBadge.svelte # Badge status │ │ │ └── Footer.svelte │ │ ├── stores/ │ │ │ ├── auth.ts # Token state │ │ │ └── theme.ts # Dark/light │ │ ├── services/ │ │ │ └── api.ts # Flask API client │ │ └── types/ │ │ ├── lesson.ts │ │ ├── auth.ts │ │ └── compiler.ts │ ├── routes/ │ │ ├── +layout.svelte # Navbar + Footer │ │ ├── +page.svelte # Home (lesson grid) │ │ ├── lesson/[slug]/ │ │ │ └── +page.svelte # Lesson viewer │ │ └── progress/ │ │ └── +page.svelte # Teacher dashboard │ └── static/ │ ├── manifest.json # PWA manifest │ └── icons/ # App icons │ └── podman-compose.yml # 3 services ``` --- ## 6. Komponen Kritis: Code Editor CodeMirror 6 adalah code editor web paling mature yang support mobile. Berikut fitur yang akan diimplementasi: ### Fitur Editor - **Syntax highlighting** — C language mode (`@codemirror/lang-cpp`) - **Line numbers** — gutter di sisi kiri - **Bracket matching** — highlight bracket pasangan - **Auto-close brackets** — otomatis tutup `{`, `(`, `[` - **Dark/Light theme** — toggle dengan `@codemirror/theme-one-dark` - **Undo/Redo** — Ctrl+Z / Ctrl+Shift+Z - **Tab handling** — insert 4 spasi (bukan tab karakter) - **Ctrl+Enter** — shortcut untuk Run - **Copy-paste prevention** — bisa diaktifkan per-lesson (configurable) - **Mobile keyboard support** — CodeMirror 6 handle virtual keyboard natively ### Perbandingan dengan Editor Saat Ini | Fitur | Saat Ini (textarea) | SvelteKit (CodeMirror 6) | |-------|:-------------------:|:------------------------:| | Syntax highlighting saat mengetik | ❌ | ✅ | | Auto-indent | ❌ | ✅ | | Bracket matching | ❌ | ✅ | | Auto-close brackets | ❌ | ✅ | | Undo/Redo | Basic browser | ✅ Full history | | Mobile keyboard | Problematic | ✅ Native support | | Dark/Light theme | Partial | ✅ Full themes | | Find & Replace | ❌ | ✅ Ctrl+F | | Multiple cursors | ❌ | ✅ | --- ## 7. Mobile Strategy: PWA ### Apa itu PWA? Progressive Web App memungkinkan web app di-install seperti native app di mobile dan desktop, tanpa harus publish ke App Store / Play Store. ### Fitur PWA yang diimplementasi 1. **Add to Home Screen** — siswa bisa install dari browser 2. **Standalone mode** — berjalan tanpa browser chrome (address bar hilang) 3. **Splash screen** — branding saat app loading 4. **Offline reading** — lesson content di-cache oleh service worker 5. **Auto-update** — service worker update otomatis saat ada versi baru ### Responsive Design | Viewport | Layout | |----------|--------| | Desktop (>1024px) | 2 kolom: lesson content (8/12) + sidebar (4/12) | | Tablet (768-1023px) | 2 kolom: lesson content (7/12) + sidebar (5/12) | | Mobile (<768px) | 1 kolom, sidebar jadi accordion expandable | ### Code Editor di Mobile - Default height 200px dengan tombol expand - CodeMirror 6 handle virtual keyboard secara native - Touch-friendly buttons (target area lebih besar) - Auto-scroll ke cursor saat virtual keyboard muncul --- ## 8. Tailscale Integration ### Arsitektur Deployment ``` Tailscale Network │ ┌────────────────▼────────────────┐ │ elemes-ts (Tailscale container) │ │ hostname: sinau-c-dev │ │ HTTPS :443 (Funnel) │ │ │ │ │ ▼ proxy │ │ elemes-frontend :3000 │ │ (SvelteKit) │ │ │ │ │ ▼ /api/* │ │ elemes-api :5000 │ │ (Flask + gcc) │ └─────────────────────────────────┘ ``` ### Akses Development - **Internal:** `http://localhost:3000` (langsung ke SvelteKit) - **External via Tailscale:** `https://sinau-c-dev..ts.net` - **Funnel (public):** Bisa diaktifkan untuk akses tanpa Tailscale client ### Perubahan dari Setup Saat Ini - Tailscale proxy target berubah dari Flask (:5000) ke SvelteKit (:3000) - SvelteKit yang meng-handle semua request client, lalu proxy `/api/*` ke Flask internal --- ## 9. Fase Implementasi ### Phase 0: Backend Decomposition **Tujuan:** Pecah Flask monolith menjadi modular API - Buat struktur `elemes/api/` - Extract services: `lesson_service.py`, `token_service.py` - Extract routes ke Blueprints: `auth.py`, `compile.py`, `lessons.py`, `progress.py` - Rewrite `app.py` sebagai factory function - Tambah `flask-cors` - Write unit tests **Output:** Flask API yang return JSON, siap dikonsumsi frontend ### Phase 1: SvelteKit Scaffolding **Tujuan:** Setup project SvelteKit dengan dependencies - Initialize project dengan TypeScript - Install CodeMirror 6, PWA plugin, marked - Configure adapter-node dan API proxy ### Phase 2: Core Components **Tujuan:** Build komponen reusable - Auth store + API service - CodeEditor (CodeMirror 6) - Navbar, Footer, LessonCard, OutputPanel ### Phase 3: Pages **Tujuan:** Build semua halaman - Home page (lesson grid + SSR) - Lesson page (content + editor + output) - Progress report (teacher dashboard) ### Phase 4: Mobile + PWA **Tujuan:** Optimasi mobile dan installability - Responsive CSS - PWA manifest + service worker - Offline lesson caching - Mobile-optimized editor ### Phase 5: Containerization + Tailscale **Tujuan:** Deploy ke containers - Frontend Dockerfile (Node 20) - API Dockerfile (Python 3.11 + gcc) - Update podman-compose.yml (3 services) - Update Tailscale config ### Phase 6: Cleanup **Tujuan:** Hapus kode lama - Remove Jinja2 templates - Remove vanilla JS - Remove flutter_app/ placeholder - Remove flask-talisman (CSP di SvelteKit) --- ## 10. Stack Teknologi ### Backend (Flask API) | Teknologi | Versi | Fungsi | |-----------|-------|--------| | Python | 3.11 | Runtime | | Flask | 2.3+ | Web framework (API only) | | Flask-CORS | latest | Cross-origin untuk SvelteKit | | Gunicorn | 21.2 | Production WSGI server | | gcc | system | C compiler | | python-markdown | 3.5 | Markdown → HTML | ### Frontend (SvelteKit) | Teknologi | Versi | Fungsi | |-----------|-------|--------| | Node.js | 20 LTS | Runtime | | SvelteKit | 2.x | Full-stack framework | | Svelte | 5.x | UI framework | | TypeScript | 5.x | Type safety | | CodeMirror 6 | 6.x | Code editor | | @vite-pwa/sveltekit | latest | PWA support | | marked | latest | Client-side markdown (jika perlu) | | highlight.js | 11.x | Syntax highlighting di konten | ### Infrastructure | Teknologi | Fungsi | |-----------|--------| | Podman | Container runtime | | Podman Compose | Container orchestration | | Tailscale | VPN + HTTPS (Funnel) | --- ## 11. API Endpoints Semua endpoint Flask akan di-prefix dengan `/api/`: ### Authentication | Method | Endpoint | Request | Response | |--------|----------|---------|----------| | POST | `/api/login` | `{token}` | `{success, student_name}` | | POST | `/api/logout` | — | `{success}` | | POST | `/api/validate-token` | `{token}` | `{success, student_name}` | ### Lessons | Method | Endpoint | Response | |--------|----------|----------| | GET | `/api/lessons` | `[{title, filename, description, completed}]` | | GET | `/api/lesson/` | `{lesson_html, exercise_html, initial_code, solution_code, expected_output, key_text, title, prev, next}` | | GET | `/api/key-text/` | `{key_text}` | ### Compilation | Method | Endpoint | Request | Response | |--------|----------|---------|----------| | POST | `/api/compile` | `{code, language?}` | `{success, output, error}` | ### Progress | Method | Endpoint | Request/Params | Response | |--------|----------|----------------|----------| | POST | `/api/track-progress` | `{token, lesson_name}` | `{success}` | | GET | `/api/progress-report` | `?token=xxx` | `{students: [{name, progress: {}}]}` | | GET | `/api/export-csv` | `?token=xxx` | CSV file download | --- ## 12. Verifikasi ### Testing Strategy 1. **Backend:** pytest di dalam container (`podman exec elemes-api pytest -v`) 2. **Frontend unit:** Vitest (`npm run test`) 3. **Frontend E2E:** Playwright (`npm run test:e2e`) 4. **Manual:** Cek di desktop dan mobile viewport ### Checklist Verifikasi - [ ] Home page menampilkan 25 lesson cards - [ ] Login dengan token berhasil - [ ] Lesson page menampilkan konten markdown - [ ] Code editor berfungsi (syntax highlighting, line numbers) - [ ] Run code → output ditampilkan - [ ] Kode benar → progress ter-track - [ ] Progress report menampilkan data siswa - [ ] Export CSV berfungsi - [ ] Responsive di mobile (375x667) - [ ] PWA installable - [ ] Tailscale Funnel accessible --- ## 13. Risiko dan Mitigasi | Risiko | Dampak | Mitigasi | |--------|--------|----------| | CodeMirror 6 bundle size di mobile | Slow initial load | Lazy-load CodeMirror hanya di lesson page | | Flask API downtime saat refactor | Siswa tidak bisa belajar | Parallel running: old templates tetap jalan | | Markdown parsing perbedaan Python vs JS | Rendering berbeda | Flask tetap parse markdown (single source of truth) | | Tailscale config berubah | Akses terputus | Backup config lama, rollback plan ready | --- *Dokumen ini adalah proposal. Implementasi dimulai setelah disetujui.*