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] ![C Logo](https://upload.wikimedia.org/wikipedia/commons/1/18/C_Programming_Language.svg) - [] ![Wrong Logo](https://upload.wikimedia.org/wikipedia/commons/c/c3/Python-logo-notext.svg) + +### 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 ![...](path) 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 >)