Elemes memerlukan fitur agar author materi dapat menyisipkan konten *embedded* (iframe dari platform luar) langsung dari markdown — baik di tubuh materi maupun di dalam slide presentasi. Contoh penggunaan: video YouTube, desain Canva, Google Docs, Figma, widget Scratch, dll.
### Infrastruktur yang Sudah Ada
Elemes **sudah punya** pipeline markdown→embed untuk `circuit` dan `flowchart`:
- **Backend** (`services/lesson_service.py`): regex fence ```circuit``` → `<div class="*-embed" data-*>`, lalu `md.markdown()` render jadi HTML.
- **Frontend** (`src/lib/actions/render*Embeds.ts`): `IntersectionObserver` ganti div → `<iframe>` lazy load.
- **Slide** sudah diparse di `lesson_service.py`, dan di dalam loop slide embed circuit/flowchart sudah diproses — jadi embed otomatis berlaku di materi **dan** slide.
**Kesimpulan feasibility:** ✅ Sangat feasible — infrastruktur sudah ada, tinggal diperluas polanya.
---
## 2. Evolusi Pendekatan
### Opsi Awal (Ditolak): URL-only fence + whitelist domain
Pendekatan pertama: user tulis URL di fence ```embed```, backend bikin div, frontend pasang iframe.
````markdown
```embed,100%,400px
https://www.youtube.com/embed/VIDEO_ID
```
````
**Masalah ditemukan saat testing:**
1.**Canva menolak di-iframe** — "canva.com refused to connect". Canva set `X-Frame-Options: DENY` untuk URL design biasa; butuh URL khusus `?embed` untuk mengizinkan iframe.
2.**Embed di slide stuck "Memuat..."** — karena iframe ditolak, `onload` tidak fire, loading text tidak dihapus.
3.**Perlu transform per-platform** — Canva butuh `?embed`, Google Docs butuh `/preview`, Figma butuh format khusus. Hardcode per-platform tidak fleksibel.
### Pendekatan Final (Dipilih): Raw HTML embed code + bleach sanitizer
Alih-alih URL, user **paste embed HTML code** siap pakai dari platform (Share → Embed):
<ahref="https://www.canva.com/..."target="_blank"rel="noopener">Judul</a> by Author
```
````
**Kelebihan:**
- User kontrol penuh (aspect ratio, style, link credit) — embed code dari platform resmi sudah optimize.
- Support Canva, YouTube, Google Docs, Figma, Scratch, dll sekaligus — tanpa hardcode transform per-platform.
- Lebih fleksibel: author bisa kustomisasi wrapper, caption, dll.
**Konsekuensi keamanan:** Raw HTML = potensi XSS. Wajib **sanitize** sebelum render. Tanpa sanitize, author bisa sisipkan `<script>`, `onclick`, `onerror`, dll.
Import `CSSSanitizer` di-bungkus `try/except ImportError`. Kalau `tinycss2` tidak terinstall di environment, aplikasi **tidak crash** — fallback ke `bleach.clean()` tanpa CSS sanitizer (tags/attrs tetap di-sanitize, hanya style CSS tidak difilter). Di production, `tinycss2` wajib ada di `requirements.txt` untuk keamanan penuh.
HTML bersih (iframe jadi) → md.markdown() → lesson_content / slides_html
↓
Frontend: langsung render via {@html} — tidak perlu action khusus
```
Backend memanggil `_process_embed_embeds()` di 4 titik agar berlaku di semua konten:
1. Loop slide (slide carousel)
2.`lesson_content` (tubuh materi)
3.`exercise_content` (latihan)
4.`lesson_info` (info pelajaran)
Frontend tidak butuh action baru — HTML sudah berisi iframe jadi dari backend. Action `renderEmbedEmbeds.ts` dari pendekatan URL-only lama sudah dihapus.
- **"Konten embed ditolak: iframe harus https."** — URL iframe pakai `http://`, ganti ke `https://`.
- **"Konten embed ditolak: domain iframe diblokir."** — Domain iframe ada di blacklist (internal/metadata endpoint).
- **"Konten embed kosong."** — Fence ```embed``` tidak berisi apa-apa.
---
## 6. Pertanyaan Umum
**Kenapa pakai raw HTML, bukan URL saja?**
Karena setiap platform punya format embed berbeda (Canva butuh `?embed`, Google Docs butuh `/preview`, Figma butuh `embed_host`). Dengan raw HTML, author paste kode siap pakai dari platform — lebih fleksibel dan tidak perlu hardcode transform per-platform di backend.
**Apakah aman?**
Ya. HTML di-sanitize pakai `bleach` (whitelist tag/attr/style) + cek domain iframe di blacklist. `<script>`, event handler (`onclick`), `javascript:` URL, dan domain berbahaya semua ditolak.
**Bisa dipakai di slide?**
Ya. Embed di dalam `---slide-start---` / `---slide-end---` otomatis diproses — backend panggil `_process_embed_embeds()` di loop slide.
**Kenapa `.generic-embed*` CSS dihapus?**
Itu CSS dari pendekatan URL-only lama (frontend bikin div + lazy iframe). Sekarang iframe sudah jadi dari backend, tidak butuh wrapper CSS khusus. `.embed-error` tetap dipertahankan untuk pesan error.
---
## 7. Riwayat Dokumen
Dokumen ini mengonsolidasi 4 file plan awal yang sudah superseded:
-`possibility-study-embed.md` — studi feasibility awal (opsi A/B/C).
-`plan-embed-implementation.md` — plan implementasi Opsi A (URL + whitelist).
-`plan-embed-rawhtml.md` — plan final pendekatan raw HTML + bleach.
Konsolidasi dilakukan agar pembaca masa depan tidak perlu membaca 4 file perjalanan; cukup 1 dokumen koheren yang menceritakan konteks, keputusan, dan hasil akhir.