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