Add flashcards feature with spaced repetition scheduling

Adds a full flashcards system: FSRS-based scheduling, cloze/basic
card types, CSV import/export, media storage, and study UI, wired
into navigation and the dashboard alongside existing question flows.
This commit is contained in:
hz
2026-07-21 15:08:52 -05:00
parent 29b89dc404
commit 48a8c8361d
41 changed files with 4458 additions and 8 deletions
+13 -2
View File
@@ -93,7 +93,7 @@ func show_screen(screen_name: String, payload: Variant = null) -> void:
func _apply_screen_margins(screen_name: String) -> void:
if screens_margin == null or screen_name.is_empty():
return
var is_fullscreen_study := screen_name == "player"
var is_fullscreen_study := screen_name in ["player", "flashcards_study"]
var margin := 0 if screen_name == "login" else (12 if is_fullscreen_study else (20 if compact_shell else 44))
screens_margin.add_theme_constant_override("margin_left", margin)
screens_margin.add_theme_constant_override("margin_right", margin)
@@ -310,6 +310,13 @@ func _build_ui() -> void:
var ccs_history := preload("res://scripts/ui/ccs_history.gd").new()
ccs_history.name = "CcsHistory"; ccs_history.main = self; screens_container.add_child(ccs_history); screens["ccs_history"] = ccs_history
var flashcards_hub := preload("res://scripts/ui/flashcards_hub.gd").new()
flashcards_hub.name = "FlashcardsHub"; flashcards_hub.main = self
screens_container.add_child(flashcards_hub); screens["flashcards_hub"] = flashcards_hub
var flashcards_study := preload("res://scripts/ui/flashcards_study.gd").new()
flashcards_study.name = "FlashcardsStudy"; flashcards_study.main = self
screens_container.add_child(flashcards_study); screens["flashcards_study"] = flashcards_study
var profile := preload("res://scripts/ui/progress_profile.gd").new()
profile.name = "ProgressProfile"
profile.main = self
@@ -404,6 +411,7 @@ func _populate_navigation(container: VBoxContainer, include_brand: bool) -> void
_add_nav_button(container, "question_bank", "Question Bank", "clipboard-list", true)
_add_nav_button(container, "ccs", "CCS Cases", "stethoscope", true)
_add_nav_button(container, "exams", "Exam Simulations", "clipboard-list", true)
_add_nav_button(container, "flashcards", "Flashcards", "clipboard-plus", true)
_add_nav_button(container, "bookmarks", "Bookmarks", "bookmark", true)
_add_nav_button(container, "notes", "Notes", "notebook-pen", true)
@@ -447,6 +455,7 @@ func _on_navigation_pressed(key: String) -> void:
"question_bank": show_screen("selection", "question_bank")
"exams": show_screen("simulation_history")
"ccs": show_screen("ccs_hub")
"flashcards": show_screen("flashcards_hub")
"performance": show_screen("profile")
"study_plan": show_screen("map")
"settings": show_screen("settings")
@@ -464,6 +473,8 @@ func _update_header_title(screen_name: String) -> void:
"selection": "Question Bank",
"simulation_history": "Exam Simulations",
"ccs_hub": "CCS Cases",
"flashcards_hub": "Flashcards",
"flashcards_study": "Flashcards",
"profile": "Profile",
"settings": "Settings",
"notes": "Notes",
@@ -555,7 +566,7 @@ func _close_account_menu() -> void:
func _screen_uses_shell(screen_name: String) -> bool:
return AuthState.is_logged_in and screen_name not in ["login", "legal_consent", "player", "ccs_player"]
return AuthState.is_logged_in and screen_name not in ["login", "legal_consent", "player", "ccs_player", "flashcards_study"]
func _update_shell_visibility(screen_name: String) -> void: