Renames locals/params that shadowed base-class properties (Node.name, Control.size, BoxContainer.alignment), prefixes an intentionally-unused parameter, and annotates a deliberate integer division so the console stays clean during development.
179 lines
5.5 KiB
GDScript
179 lines
5.5 KiB
GDScript
extends Control
|
|
|
|
# Prefilled basic-card editor opened from the question toolbar once a question's answer has
|
|
# been resolved. Saving creates a new flashcard note in the user's default deck; the user can
|
|
# move/retag it from the flashcards library afterward.
|
|
|
|
const Brand := preload("res://scripts/ui/medmind_style.gd")
|
|
const Style := preload("res://scripts/ui/ui_style.gd")
|
|
|
|
signal saved
|
|
|
|
var front_input: TextEdit
|
|
var back_input: TextEdit
|
|
var tags_input: LineEdit
|
|
var image_checkbox: CheckBox
|
|
var error_label: Label
|
|
var save_button: Button
|
|
var pending_image_asset_id := ""
|
|
var source_question_external_id := ""
|
|
|
|
|
|
func _ready() -> void:
|
|
visible = false
|
|
top_level = true
|
|
mouse_filter = Control.MOUSE_FILTER_STOP
|
|
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
var dim := ColorRect.new()
|
|
dim.color = Color(0, 0, 0, 0.72)
|
|
dim.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
dim.gui_input.connect(func(event: InputEvent):
|
|
if event is InputEventMouseButton and event.pressed:
|
|
hide_dialog())
|
|
add_child(dim)
|
|
var center := CenterContainer.new()
|
|
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
add_child(center)
|
|
var panel := PanelContainer.new()
|
|
panel.custom_minimum_size = Vector2(600, 520)
|
|
panel.add_theme_stylebox_override("panel", Brand.panel_box(UIThemeService.is_dark_mode()))
|
|
center.add_child(panel)
|
|
var content := VBoxContainer.new()
|
|
content.add_theme_constant_override("separation", 10)
|
|
panel.add_child(content)
|
|
|
|
var header := HBoxContainer.new()
|
|
content.add_child(header)
|
|
var title := Label.new()
|
|
title.text = "Save as flashcard"
|
|
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
title.add_theme_font_size_override("font_size", 22)
|
|
header.add_child(title)
|
|
var close_button := Button.new()
|
|
close_button.icon = preload("res://assets/icons/lucide/x.svg")
|
|
close_button.custom_minimum_size = Vector2(40, 40)
|
|
close_button.pressed.connect(hide_dialog)
|
|
header.add_child(close_button)
|
|
|
|
content.add_child(_field_label("Front"))
|
|
front_input = TextEdit.new()
|
|
front_input.custom_minimum_size = Vector2(0, 110)
|
|
front_input.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
|
|
content.add_child(front_input)
|
|
|
|
content.add_child(_field_label("Back"))
|
|
back_input = TextEdit.new()
|
|
back_input.custom_minimum_size = Vector2(0, 140)
|
|
back_input.wrap_mode = TextEdit.LINE_WRAPPING_BOUNDARY
|
|
content.add_child(back_input)
|
|
|
|
content.add_child(_field_label("Tags (comma separated)"))
|
|
tags_input = LineEdit.new()
|
|
content.add_child(tags_input)
|
|
|
|
image_checkbox = CheckBox.new()
|
|
image_checkbox.text = "Attach question image"
|
|
image_checkbox.visible = false
|
|
content.add_child(image_checkbox)
|
|
|
|
error_label = Label.new()
|
|
error_label.add_theme_color_override("font_color", Style.RED)
|
|
error_label.visible = false
|
|
content.add_child(error_label)
|
|
|
|
save_button = Button.new()
|
|
save_button.text = "Save Flashcard"
|
|
save_button.custom_minimum_size = Vector2(0, 48)
|
|
Style.style_primary_button(save_button)
|
|
save_button.pressed.connect(_save)
|
|
content.add_child(save_button)
|
|
|
|
FlashcardService.note_saved.connect(_on_saved)
|
|
FlashcardService.request_failed.connect(_on_failed)
|
|
|
|
|
|
func _field_label(text: String) -> Label:
|
|
var label := Label.new()
|
|
label.text = text
|
|
label.add_theme_font_size_override("font_size", 13)
|
|
return label
|
|
|
|
|
|
func open_for_question(question: Dictionary, _current_answer: Dictionary) -> void:
|
|
var stem := str(question.get("stem", "")).strip_edges()
|
|
var lead_in := str(question.get("lead_in", "")).strip_edges()
|
|
front_input.text = "%s\n\n%s" % [stem, lead_in] if not lead_in.is_empty() else stem
|
|
|
|
var choices: Array = question.get("choices", [])
|
|
var correct_id := str(question.get("correct_choice_id", ""))
|
|
var correct_text := ""
|
|
for choice in choices:
|
|
if str(choice.get("id", "")) == correct_id:
|
|
correct_text = str(choice.get("text", ""))
|
|
break
|
|
var takeaway := str(question.get("key_takeaway", "")).strip_edges()
|
|
if takeaway.is_empty():
|
|
takeaway = str(question.get("explanation", "")).strip_edges()
|
|
back_input.text = "%s\n\n%s" % [correct_text, takeaway] if not takeaway.is_empty() else correct_text
|
|
|
|
var tags: Array = []
|
|
var system := str(question.get("system", "")).strip_edges()
|
|
if not system.is_empty():
|
|
tags.append(system)
|
|
for tag in question.get("tags", []):
|
|
var trimmed := str(tag).strip_edges()
|
|
if not trimmed.is_empty() and not tags.has(trimmed):
|
|
tags.append(trimmed)
|
|
tags_input.text = ", ".join(PackedStringArray(tags))
|
|
|
|
var media: Array = question.get("media", [])
|
|
pending_image_asset_id = str(media[0]) if media.size() > 0 else ""
|
|
image_checkbox.visible = not pending_image_asset_id.is_empty()
|
|
image_checkbox.button_pressed = false
|
|
source_question_external_id = str(question.get("id", ""))
|
|
|
|
error_label.visible = false
|
|
visible = true
|
|
|
|
|
|
func hide_dialog() -> void:
|
|
visible = false
|
|
|
|
|
|
func _save() -> void:
|
|
var tags: Array = []
|
|
for tag in tags_input.text.split(","):
|
|
var trimmed := tag.strip_edges()
|
|
if not trimmed.is_empty():
|
|
tags.append(trimmed)
|
|
var images: Array = []
|
|
if image_checkbox.button_pressed and not pending_image_asset_id.is_empty():
|
|
images.append({"kind": "question", "assetId": pending_image_asset_id})
|
|
var payload := {
|
|
"deckId": "",
|
|
"stableId": "",
|
|
"type": "basic",
|
|
"prompt": front_input.text,
|
|
"answer": back_input.text,
|
|
"extra": "",
|
|
"tags": tags,
|
|
"images": images,
|
|
"sourceQuestionExternalId": source_question_external_id,
|
|
"expectedRevision": null
|
|
}
|
|
FlashcardService.save_note("", payload)
|
|
|
|
|
|
func _on_saved(_note: Dictionary) -> void:
|
|
if not visible:
|
|
return
|
|
hide_dialog()
|
|
saved.emit()
|
|
|
|
|
|
func _on_failed(_tag: String, message: String) -> void:
|
|
if not visible:
|
|
return
|
|
error_label.text = message
|
|
error_label.visible = true
|