diff --git a/documentation.md b/documentation.md
index af24596..5ecf93a 100644
--- a/documentation.md
+++ b/documentation.md
@@ -473,7 +473,10 @@ Untuk memudahkan pengajar, Elemes menyediakan parser Markdown khusus di `lesson_
- Jika ditemukan pola `- []` atau `- [x]`, elemen diparse sebagai `type: 'mcq'`.
- Jika tidak, diparse sebagai `type: 'flashcard'`.
4. **Explanation**: Menangkap blok kutipan `>` sebagai penjelasan yang muncul setelah kuis dijawab.
-5. **Rendering**: Pertanyaan dan jawaban di-render menggunakan filter Markdown standar (mendukung formatting, code snippets, dll).
+5. **Images**: Mendukung penambahan gambar melalui:
+ - Keyword `image: URL` tepat di bawah heading pertanyaan (akan tampil sebagai gambar utama).
+ - Syntax Markdown standar `` di dalam teks pertanyaan, opsi, atau penjelasan.
+6. **Rendering**: Pertanyaan dan jawaban di-render menggunakan filter Markdown standar (mendukung formatting, code snippets, dll).
### Quiz State Management
diff --git a/frontend/src/routes/lesson/[slug]/QuizTab.svelte b/frontend/src/routes/lesson/[slug]/QuizTab.svelte
index 222f5f3..c12d783 100644
--- a/frontend/src/routes/lesson/[slug]/QuizTab.svelte
+++ b/frontend/src/routes/lesson/[slug]/QuizTab.svelte
@@ -11,6 +11,7 @@
question?: string;
options?: Option[];
explanation?: string;
+ image?: string;
}
interface Props {
@@ -203,6 +204,9 @@
Pertanyaan
-
{@html currentCard.front}
+
+ {#if currentCard.image}
+

+ {/if}
+ {@html currentCard.front}
+
Klik untuk melihat jawaban
@@ -349,7 +358,17 @@
.flashcard-back { transform: rotateY(180deg); background: var(--color-bg); }
.side-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-muted); margin-bottom: 1rem; font-weight: 700; border-bottom: 1px solid var(--color-border); padding-bottom: 0.5rem; }
.card-content { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; font-size: 1.25rem; color: var(--color-text); line-height: 1.5; }
- :global(.card-content code) { background: var(--color-bg); border: 1px solid var(--color-border); padding: 0.2rem 0.4rem; border-radius: 4px; font-family: var(--font-mono); }
+ .quiz-image {
+ max-width: 100%;
+ max-height: 300px;
+ object-fit: contain;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+ }
+
+ :global(.card-content img) { max-width: 100%; height: auto; border-radius: 8px; margin: 1rem 0; }
+ :global(.option-text img) { max-width: 100%; height: auto; border-radius: 4px; display: block; margin: 0.5rem 0; }
:global(.card-content p) { margin: 0; }
.flip-hint { font-size: 0.75rem; color: var(--color-text-muted); margin-top: 1rem; font-style: italic; }
diff --git a/services/lesson_service.py b/services/lesson_service.py
index 191bbb5..bc26d06 100644
--- a/services/lesson_service.py
+++ b/services/lesson_service.py
@@ -343,6 +343,12 @@ def _parse_flashcards(text):
option_pattern = re.compile(r'^\s*-\s*\[([ xX]?)\]\s*(.*)$', re.MULTILINE)
options = option_pattern.findall(body)
+ # Check for image: URL
+ image_match = re.search(r'^\s*image:\s*(.*)$', body, re.MULTILINE)
+ image_url = image_match.group(1).strip() if image_match else ""
+ if image_match:
+ body = re.sub(r'^\s*image:\s*.*$', '', body, flags=re.MULTILINE).strip()
+
# Check for explanation (blockquote starting with >)
explanation_match = re.search(r'^\s*>\s*(.*)$', body, re.MULTILINE | re.DOTALL)
explanation = explanation_match.group(1).strip() if explanation_match else ""
@@ -361,7 +367,8 @@ def _parse_flashcards(text):
'type': 'mcq',
'question': md.markdown(question, extensions=MD_EXTENSIONS),
'options': parsed_options,
- 'explanation': md.markdown(explanation, extensions=MD_EXTENSIONS) if explanation else ""
+ 'explanation': md.markdown(explanation, extensions=MD_EXTENSIONS) if explanation else "",
+ 'image': image_url
})
else:
# It's a simple Flashcard
@@ -374,7 +381,8 @@ def _parse_flashcards(text):
'type': 'flashcard',
'front': md.markdown(question, extensions=MD_EXTENSIONS),
'back': md.markdown(clean_back, extensions=MD_EXTENSIONS),
- 'explanation': md.markdown(explanation, extensions=MD_EXTENSIONS) if explanation else ""
+ 'explanation': md.markdown(explanation, extensions=MD_EXTENSIONS) if explanation else "",
+ 'image': image_url
})
return flashcards