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.
60 lines
3.0 KiB
GDScript
60 lines
3.0 KiB
GDScript
extends SceneTree
|
|
|
|
# Drives the flashcards study screen's reveal/grading render logic and keyboard mapping
|
|
# directly (no network): the screen is added to the tree so _ready() builds its controls and
|
|
# wires the FlashcardService signal connections, but no method that would send an HTTP
|
|
# request (submit_review/request_next_card) is invoked.
|
|
|
|
var failures := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
var Study := load("res://scripts/ui/flashcards_study.gd")
|
|
var screen = Study.new()
|
|
screen._build()
|
|
|
|
# Keyboard grading shortcuts: 1-4 map to Again/Hard/Good/Easy; everything else is inert.
|
|
_assert(Study.rating_for_keycode(KEY_1) == "again", "1 should grade Again")
|
|
_assert(Study.rating_for_keycode(KEY_2) == "hard", "2 should grade Hard")
|
|
_assert(Study.rating_for_keycode(KEY_3) == "good", "3 should grade Good")
|
|
_assert(Study.rating_for_keycode(KEY_4) == "easy", "4 should grade Easy")
|
|
_assert(Study.rating_for_keycode(KEY_5) == "", "keys outside 1-4 should not grade a card")
|
|
|
|
# Basic card: front-first, then reveal shows answer + extra and the four graded options.
|
|
screen.current_note = {"type": "basic", "prompt": "2 + 2 = ?", "answer": "4", "extra": "Basic arithmetic"}
|
|
screen.current_card = {"id": "card-1", "variant": 0, "revision": 1}
|
|
screen.rating_options = [
|
|
{"rating": "again", "intervalLabel": "<1m"}, {"rating": "hard", "intervalLabel": "6m"},
|
|
{"rating": "good", "intervalLabel": "1d"}, {"rating": "easy", "intervalLabel": "4d"}
|
|
]
|
|
screen._render_card()
|
|
_assert(screen.front_label.text == "2 + 2 = ?", "the front should show only the prompt before reveal")
|
|
_assert(screen.reveal_button.visible, "the reveal button should be visible before the answer is shown")
|
|
_assert(not screen.grade_row.visible, "grading buttons should be hidden before reveal")
|
|
_assert(not screen.back_label.visible, "the back should stay hidden before reveal")
|
|
|
|
screen._reveal()
|
|
_assert(screen.back_label.text == "4\n\nBasic arithmetic", "reveal should show the answer plus extra")
|
|
_assert(screen.grade_row.visible, "grading buttons should appear after reveal")
|
|
_assert(not screen.reveal_button.visible, "the reveal button should hide once the card is revealed")
|
|
_assert(screen.grade_buttons["good"].text.contains("1d"), "each grade button should show its computed next interval")
|
|
|
|
# Cloze card: only the card's own variant is hidden on the front; back reveals everything.
|
|
screen.current_note = {"type": "cloze", "prompt": "{{c1::Paris}} is the capital of France", "answer": "", "extra": ""}
|
|
screen.current_card = {"id": "card-2", "variant": 1, "revision": 1}
|
|
screen.revealed = false
|
|
screen._render_card()
|
|
_assert(screen.front_label.text == "[...] is the capital of France", "cloze front should hide only its own variant")
|
|
screen._reveal()
|
|
_assert(screen.back_label.text == "Paris is the capital of France", "cloze back should reveal every deletion")
|
|
|
|
screen.free()
|
|
quit(1 if failures > 0 else 0)
|
|
|
|
|
|
func _assert(condition: bool, message: String) -> void:
|
|
if condition:
|
|
return
|
|
failures += 1
|
|
push_error(message)
|