From 438d96ef139f5e483be318feda79690cfd5b0bba Mon Sep 17 00:00:00 2001 From: a2nr Date: Sun, 10 May 2026 20:38:35 +0700 Subject: [PATCH] feat: update image capability inside quiz --- documentation.md | 5 +++- .../src/routes/lesson/[slug]/QuizTab.svelte | 23 +++++++++++++++++-- services/lesson_service.py | 12 ++++++++-- 3 files changed, 35 insertions(+), 5 deletions(-) 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 `![alt](url)` 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
+ {#if currentCard.image} + Pertanyaan + {/if} {@html currentCard.question}
@@ -245,7 +249,12 @@
Pertanyaan
-
{@html currentCard.front}
+
+ {#if currentCard.image} + Pertanyaan + {/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