extends VBoxContainer const Brand := preload("res://scripts/ui/medmind_style.gd") const CORRECT_ICON := preload("res://assets/icons/lucide/circle-check.svg") const PROBABILITY_ICON := preload("res://assets/icons/lucide/activity.svg") const SEEN_ICON := preload("res://assets/icons/lucide/eye.svg") const CATEGORY_ICON := preload("res://assets/icons/lucide/chart-no-axes-column-increasing.svg") const REVIEW_ICON := preload("res://assets/icons/lucide/clipboard-list.svg") const CATEGORY_PALETTE := [Brand.ROYAL_BLUE, Brand.SUCCESS, Brand.AMBER, Brand.MAGENTA, Brand.SKY_BLUE] var main: Control var content: VBoxContainer var current_simulation := {} var return_screen := "selection" func _ready() -> void: set_anchors_preset(Control.PRESET_FULL_RECT) add_theme_constant_override("separation", 0) var scroll := ScrollContainer.new() scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED add_child(scroll) content = VBoxContainer.new() content.size_flags_horizontal = Control.SIZE_EXPAND_FILL content.add_theme_constant_override("separation", 20) scroll.add_child(content) UIThemeService.theme_changed.connect(func(_dark: bool): _render()) func on_show(payload: Variant = null) -> void: var wrapper: Dictionary = payload if typeof(payload) == TYPE_DICTIONARY else {} var simulation: Dictionary = wrapper.get("simulation", wrapper) if not wrapper.is_empty() else ExamSimulationService.simulation return_screen = str(wrapper.get("return_screen", "selection")) current_simulation = simulation _render() func _render() -> void: _clear(content) var dark := UIThemeService.is_dark_mode() var score: Dictionary = current_simulation.get("score", {}) content.add_child(_build_hero_card(dark, score)) content.add_child(_build_stats_row(dark, score)) content.add_child(_build_disclaimer(dark, score)) content.add_child(_build_category_card(dark, score)) content.add_child(_build_actions()) func _build_hero_card(dark: bool, score: Dictionary) -> PanelContainer: var predicted := int(score.get("predictedScore", 0)) var low := int(score.get("rangeLow", 0)) var high := int(score.get("rangeHigh", 0)) var row := HBoxContainer.new() row.add_theme_constant_override("separation", 24) var copy := VBoxContainer.new() copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL copy.add_theme_constant_override("separation", 10) row.add_child(copy) copy.add_child(_label("STEP 3 DAY 1 SIMULATION COMPLETE", 13, Brand.SKY_BLUE, true)) var headline := _label("Provisional Step 3 estimate: %d" % predicted, 26, Brand.text_color(dark), true) headline.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART copy.add_child(headline) var badge := _label("%d-%d Predicted score range" % [low, high], 13, Brand.text_color(dark)) badge.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN badge.add_theme_stylebox_override("normal", Brand.badge_box(dark, Brand.ROYAL_BLUE)) copy.add_child(badge) row.add_child(_score_medallion(dark, predicted, Brand.ROYAL_BLUE)) return _card(dark, row, Brand.ROYAL_BLUE, true) func _score_medallion(dark: bool, score: int, accent: Color) -> Control: var wrap := Control.new() wrap.custom_minimum_size = Vector2(120, 120) wrap.size_flags_horizontal = Control.SIZE_SHRINK_CENTER wrap.size_flags_vertical = Control.SIZE_SHRINK_CENTER var panel := PanelContainer.new() panel.set_anchors_preset(Control.PRESET_FULL_RECT) panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 60, true)) wrap.add_child(panel) var center := CenterContainer.new() center.set_anchors_preset(Control.PRESET_FULL_RECT) panel.add_child(center) center.add_child(_label(str(score), 34, Brand.DARK_TEXT, true)) return wrap func _build_stats_row(dark: bool, score: Dictionary) -> HBoxContainer: var row := HBoxContainer.new() row.add_theme_constant_override("separation", 16) var correct := int(score.get("correct", 0)) var total := int(score.get("total", 0)) var probability := int(round(float(score.get("passProbability", 0.0)) * 100.0)) var seen := int(score.get("seenCount", 0)) row.add_child(_stat_card(dark, CORRECT_ICON, Brand.ROYAL_BLUE, "%d / %d" % [correct, total], "Correct")) row.add_child(_stat_card(dark, PROBABILITY_ICON, Brand.SUCCESS, "%d%%" % probability, "Heuristic probability of reaching 200+")) row.add_child(_stat_card(dark, SEEN_ICON, Brand.SKY_BLUE, str(seen), "Previously seen questions")) return row func _stat_card(dark: bool, icon: Texture2D, accent: Color, value: String, caption: String) -> PanelContainer: var inner := HBoxContainer.new() inner.add_theme_constant_override("separation", 12) var icon_panel := PanelContainer.new() icon_panel.custom_minimum_size = Vector2(44, 44) icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 22, true)) var icon_rect := TextureRect.new() icon_rect.texture = icon icon_rect.custom_minimum_size = Vector2(20, 20) icon_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE icon_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED icon_rect.modulate = Brand.DARK_TEXT icon_panel.add_child(icon_rect) inner.add_child(icon_panel) var copy := VBoxContainer.new() copy.add_theme_constant_override("separation", 2) copy.size_flags_horizontal = Control.SIZE_EXPAND_FILL copy.add_child(_label(value, 20, Brand.text_color(dark), true)) var caption_label := _label(caption, 12, Brand.muted_color(dark)) caption_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART copy.add_child(caption_label) inner.add_child(copy) return _card(dark, inner) func _build_disclaimer(dark: bool, score: Dictionary) -> Label: var raw_accuracy := float(score.get("rawAccuracy", 0.0)) * 100.0 var adjusted_accuracy := float(score.get("difficultyAdjustedAccuracy", 0.0)) * 100.0 var label := _label( "%.1f%% raw accuracy ยท %.1f%% difficulty-adjusted accuracy\nThis transparent, unvalidated educational heuristic is not an official USMLE or NBME score and excludes CCS performance." % [raw_accuracy, adjusted_accuracy], 12, Brand.muted_color(dark)) label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART return label func _build_category_card(dark: bool, score: Dictionary) -> PanelContainer: var column := VBoxContainer.new() column.add_theme_constant_override("separation", 14) var heading := HBoxContainer.new() heading.add_theme_constant_override("separation", 8) var heading_icon := TextureRect.new() heading_icon.texture = CATEGORY_ICON heading_icon.custom_minimum_size = Vector2(20, 20) heading_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE heading_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED heading_icon.modulate = Brand.ROYAL_BLUE heading.add_child(heading_icon) heading.add_child(_label("Performance by content area", 18, Brand.text_color(dark), true)) column.add_child(heading) var grid := GridContainer.new() grid.columns = 2 grid.add_theme_constant_override("h_separation", 32) grid.add_theme_constant_override("v_separation", 14) var categories: Dictionary = score.get("categories", {}) var index := 0 for category_name in categories: var value: Dictionary = categories[category_name] grid.add_child(_category_row(dark, str(category_name), value, index)) index += 1 if categories.is_empty(): column.add_child(_label("No content-area breakdown is available for this simulation.", 13, Brand.muted_color(dark))) else: column.add_child(grid) return _card(dark, column) func _category_row(dark: bool, category_name: String, value: Dictionary, index: int) -> HBoxContainer: var row := HBoxContainer.new() row.add_theme_constant_override("separation", 10) row.size_flags_horizontal = Control.SIZE_EXPAND_FILL var accent: Color = CATEGORY_PALETTE[index % CATEGORY_PALETTE.size()] var dot := PanelContainer.new() dot.custom_minimum_size = Vector2(10, 10) dot.size_flags_vertical = Control.SIZE_SHRINK_CENTER dot.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, accent, 5, true)) row.add_child(dot) var correct := int(value.get("correct", 0)) var total := int(value.get("total", 0)) var percent := int(round(100.0 * float(correct) / float(total))) if total > 0 else 0 var title := _label(category_name, 13, Brand.text_color(dark)) title.custom_minimum_size.x = 200 title.clip_text = true title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS row.add_child(title) var bar := ProgressBar.new() bar.show_percentage = false bar.min_value = 0.0 bar.max_value = 100.0 bar.value = percent bar.custom_minimum_size = Vector2(90, 7) bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL bar.size_flags_vertical = Control.SIZE_SHRINK_CENTER _style_progress(bar, accent, dark) row.add_child(bar) var detail := _label("%d%% %d/%d" % [percent, correct, total], 12, accent if dark else accent.darkened(0.15)) detail.custom_minimum_size.x = 76 detail.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT row.add_child(detail) return row func _build_actions() -> HBoxContainer: var row := HBoxContainer.new() row.add_theme_constant_override("separation", 12) var review_button := Button.new() review_button.text = "Review Questions" review_button.icon = REVIEW_ICON review_button.custom_minimum_size = Vector2(220, 52) review_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN _style_gradient_button(review_button) review_button.pressed.connect(func(): main.show_screen("simulation_review", {"simulation": current_simulation, "return_screen": return_screen})) row.add_child(review_button) return row func _card(dark: bool, child: Control, accent: Color = Brand.ROYAL_BLUE, active: bool = false) -> PanelContainer: var panel := PanelContainer.new() panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL var box := Brand.content_card_box(dark, active, accent) box.content_margin_left = 20 box.content_margin_right = 20 box.content_margin_top = 18 box.content_margin_bottom = 18 panel.add_theme_stylebox_override("panel", box) panel.add_child(child) return panel func _label(text: String, font_size: int, color: Color, _bold: bool = false) -> Label: var label := Label.new() label.text = text label.add_theme_font_override("font", Brand.font) label.add_theme_font_size_override("font_size", font_size) label.add_theme_color_override("font_color", color) return label func _style_progress(bar: ProgressBar, accent: Color, dark: bool) -> void: var background := StyleBoxFlat.new() background.bg_color = Color("26324a") if dark else Color("e2e8f0") background.set_corner_radius_all(4) var fill := StyleBoxFlat.new() fill.bg_color = accent fill.set_corner_radius_all(4) bar.add_theme_stylebox_override("background", background) bar.add_theme_stylebox_override("fill", fill) func _style_gradient_button(button: Button) -> void: Brand.apply_font(button) button.add_theme_font_size_override("font_size", 15) button.add_theme_stylebox_override("normal", Brand.gradient_button_box()) button.add_theme_stylebox_override("hover", Brand.gradient_button_box()) button.add_theme_stylebox_override("pressed", Brand.gradient_button_box()) button.add_theme_color_override("font_color", Color.WHITE) button.add_theme_color_override("font_hover_color", Color.WHITE) func _clear(container: Control) -> void: for child in container.get_children(): container.remove_child(child) child.free()