diff --git a/config.py b/config.py
index c7e801b..36319a9 100644
--- a/config.py
+++ b/config.py
@@ -7,3 +7,9 @@ import os
CONTENT_DIR = os.environ.get('CONTENT_DIR', 'content')
TOKENS_FILE = os.environ.get('TOKENS_FILE', 'tokens.csv')
+
+# Assets directory: derived from parent of CONTENT_DIR
+# E.g. CONTENT_DIR='content' → ASSETS_DIR='assets'
+# CONTENT_DIR='examples/content' → ASSETS_DIR='examples/assets'
+# CONTENT_DIR='/app/content' → ASSETS_DIR='/app/assets'
+ASSETS_DIR = os.path.join(os.path.dirname(CONTENT_DIR.rstrip(os.sep)), 'assets')
diff --git a/examples/assets/demo_quiz.png b/examples/assets/demo_quiz.png
new file mode 100644
index 0000000..4911d38
Binary files /dev/null and b/examples/assets/demo_quiz.png differ
diff --git a/examples/assets/put_your_image.here b/examples/assets/put_your_image.here
deleted file mode 100644
index e69de29..0000000
diff --git a/examples/content/dasar/quiz_test.md b/examples/content/dasar/quiz_test.md
index 51cbf59..5a21fbf 100644
--- a/examples/content/dasar/quiz_test.md
+++ b/examples/content/dasar/quiz_test.md
@@ -26,6 +26,15 @@ Menandakan bahwa program telah selesai berjalan dengan sukses tanpa ada error.
### Pilih gambar yang melambangkan bahasa C:
- [x] 
- [] 
+
+### Apa warna langit pada siang hari?
+image: demo_quiz.png
+- [x] Biru
+- [] Hijau
+- [] Merah
+- [] Kuning
+> Penjelasan: Langit terlihat biru karena hamburan Rayleigh — cahaya matahari dihamburkan oleh atmosfer bumi.
+
---END_QUIZ_FLASHCARD---
---EXERCISE---
diff --git a/frontend/src/routes/lesson/[slug]/+page.svelte b/frontend/src/routes/lesson/[slug]/+page.svelte
index 7bb6786..aee0be5 100644
--- a/frontend/src/routes/lesson/[slug]/+page.svelte
+++ b/frontend/src/routes/lesson/[slug]/+page.svelte
@@ -297,7 +297,7 @@
{/if}
{#if mgr.data?.active_tabs?.includes('quiz')}
-
+
.quiz-container {
+ flex: 1;
+ min-height: 0;
padding: 1.5rem;
- height: 100%;
display: flex;
flex-direction: column;
background: var(--color-bg);
diff --git a/frontend/src/routes/lesson/[slug]/lesson.css b/frontend/src/routes/lesson/[slug]/lesson.css
index dc2b9e3..02dd7b6 100644
--- a/frontend/src/routes/lesson/[slug]/lesson.css
+++ b/frontend/src/routes/lesson/[slug]/lesson.css
@@ -80,7 +80,7 @@
display: flex;
flex-direction: column;
overflow: hidden;
- max-height: 85vh;
+ height: 70vh;
}
.editor-area .editor-body {
flex: 1;
@@ -291,15 +291,18 @@
}
.velxio-panel,
-.flowchart-panel {
+.flowchart-panel,
+.quiz-panel {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
position: relative;
+ overflow: hidden;
}
.velxio-panel.tab-hidden,
-.flowchart-panel.tab-hidden {
+.flowchart-panel.tab-hidden,
+.quiz-panel.tab-hidden {
display: none;
}
.velxio-iframe {
@@ -468,4 +471,4 @@
color: var(--color-text-muted);
margin-bottom: 1.5rem;
line-height: 1.4;
-}
\ No newline at end of file
+}
diff --git a/routes/lessons.py b/routes/lessons.py
index 48c7d48..30797b8 100644
--- a/routes/lessons.py
+++ b/routes/lessons.py
@@ -8,7 +8,7 @@ from flask import Blueprint, request, jsonify, send_from_directory
from werkzeug.utils import secure_filename
from compiler import compiler_factory
-from config import CONTENT_DIR
+from config import CONTENT_DIR, ASSETS_DIR
from services.lesson_service import (
get_ordered_lessons_with_learning_objectives,
render_markdown_content,
@@ -225,5 +225,5 @@ def get_key_text(filename):
@lessons_bp.route('/assets/')
def send_assets(path):
- """Serve asset files (images, etc.)."""
- return send_from_directory('assets', path)
+ """Serve asset files (images, etc.) from ASSETS_DIR."""
+ return send_from_directory(ASSETS_DIR, path)
diff --git a/services/lesson_service.py b/services/lesson_service.py
index f4a6b94..b9f4efc 100644
--- a/services/lesson_service.py
+++ b/services/lesson_service.py
@@ -481,14 +481,30 @@ def _parse_flashcards(text):
if not question:
continue
+ # Initialize image_url - will be set from markdown image or 'image:' directive
+ image_url = ""
+
+ # Extract image from markdown syntax  in question
+ md_image_match = re.search(r'!\[.*?\]\(([^)]+)\)', question)
+ if md_image_match:
+ md_image_path = md_image_match.group(1).strip()
+ # Jika path lokal (/assets/ atau bare filename) dan belum ada image dari 'image:' directive
+ if md_image_path.startswith('/assets/') or (not md_image_path.startswith(('http://', 'https://')) and not image_url):
+ image_url = md_image_path
+
# Check for MCQ options: - [ ] or - [x]
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:
+ image_url = image_match.group(1).strip() # Override jika ada 'image:' directive
+ if image_url.startswith('/assets/'):
+ # Path sudah lengkap, tidak perlu konversi
+ pass
+ elif not image_url.startswith(('http://', 'https://', '/')):
+ image_url = f'/assets/{image_url}'
body = re.sub(r'^\s*image:\s*.*$', '', body, flags=re.MULTILINE).strip()
# Check for explanation (blockquote starting with >)