extends VBoxContainer const Style := preload("res://scripts/ui/ui_style.gd") const Brand := preload("res://scripts/ui/medmind_style.gd") const CASE_ICON := preload("res://assets/icons/lucide/stethoscope.svg") const REVIEW_ICON := preload("res://assets/icons/lucide/chart-no-axes-column-increasing.svg") var main: Control var page: VBoxContainer var active_section: PanelContainer var active_content: VBoxContainer var history_section: PanelContainer var history_header: BoxContainer var table_header: HBoxContainer var history_list: VBoxContainer var status_label: Label var load_more_button: Button var filter_option: OptionButton var sort_option: OptionButton var abandon_dialog: ConfirmationDialog var pending_action := "" var pending_attempt_id := "" var history_status := "all" var history_sort := "newest" var compact_layout := false func _ready() -> void: set_anchors_preset(Control.PRESET_FULL_RECT) size_flags_horizontal = Control.SIZE_EXPAND_FILL size_flags_vertical = Control.SIZE_EXPAND_FILL add_theme_constant_override("separation", 0) _build_ui() _connect_services() resized.connect(_update_responsive_layout) UIThemeService.theme_changed.connect(func(_dark_mode: bool): refresh_theme_controls()) call_deferred("_update_responsive_layout") func _build_ui() -> void: 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) page = VBoxContainer.new() page.size_flags_horizontal = Control.SIZE_EXPAND_FILL page.add_theme_constant_override("separation", 18) scroll.add_child(page) active_section = PanelContainer.new() active_section.name = "ActiveCcsSection" page.add_child(active_section) active_content = VBoxContainer.new() active_content.add_theme_constant_override("separation", 10) active_section.add_child(active_content) history_section = PanelContainer.new() history_section.name = "CcsHistorySection" history_section.size_flags_vertical = Control.SIZE_EXPAND_FILL page.add_child(history_section) var history_content := VBoxContainer.new() history_content.add_theme_constant_override("separation", 12) history_section.add_child(history_content) history_header = BoxContainer.new() history_header.add_theme_constant_override("separation", 10) history_content.add_child(history_header) var heading := Label.new() heading.text = "Case History" heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL Brand.apply_font(heading) heading.add_theme_font_size_override("font_size", 21) history_header.add_child(heading) filter_option = OptionButton.new() filter_option.name = "HistoryFilter" filter_option.custom_minimum_size = Vector2(175, 42) filter_option.add_item("All Cases") filter_option.set_item_metadata(0, "all") filter_option.add_item("Completed") filter_option.set_item_metadata(1, "completed") filter_option.add_item("Abandoned") filter_option.set_item_metadata(2, "abandoned") filter_option.item_selected.connect(_on_filter_selected) history_header.add_child(filter_option) sort_option = OptionButton.new() sort_option.name = "HistorySort" sort_option.custom_minimum_size = Vector2(165, 42) sort_option.add_item("Newest First") sort_option.set_item_metadata(0, "newest") sort_option.add_item("Oldest First") sort_option.set_item_metadata(1, "oldest") sort_option.item_selected.connect(_on_sort_selected) history_header.add_child(sort_option) status_label = Label.new() status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART Brand.apply_font(status_label) status_label.add_theme_font_size_override("font_size", 15) history_content.add_child(status_label) table_header = _build_table_header() history_content.add_child(table_header) history_list = VBoxContainer.new() history_list.add_theme_constant_override("separation", 0) history_content.add_child(history_list) load_more_button = Button.new() load_more_button.text = "Show more" load_more_button.visible = false load_more_button.custom_minimum_size = Vector2(150, 42) load_more_button.size_flags_horizontal = Control.SIZE_SHRINK_CENTER load_more_button.pressed.connect(_load_more) history_content.add_child(load_more_button) abandon_dialog = ConfirmationDialog.new() abandon_dialog.title = "Abandon case?" abandon_dialog.dialog_text = "This case cannot be resumed and will not receive a score. It will remain visible in your case history." abandon_dialog.ok_button_text = "Abandon Case" abandon_dialog.cancel_button_text = "Keep Case" abandon_dialog.confirmed.connect(_confirm_abandon) add_child(abandon_dialog) refresh_theme_controls() func _connect_services() -> void: if not CCSService.cases_changed.is_connected(_render_active_section): CCSService.cases_changed.connect(_render_active_section) if not CCSService.attempt_changed.is_connected(_on_attempt_changed): CCSService.attempt_changed.connect(_on_attempt_changed) if not CCSService.attempt_abandoned.is_connected(_on_attempt_abandoned): CCSService.attempt_abandoned.connect(_on_attempt_abandoned) if not CCSService.history_changed.is_connected(_render_history): CCSService.history_changed.connect(_render_history) if not CCSService.prediction_changed.is_connected(_render_active_section): CCSService.prediction_changed.connect(_render_active_section) if not CCSService.rating_changed.is_connected(_render_active_section): CCSService.rating_changed.connect(_render_active_section) if not CCSService.request_failed.is_connected(_on_request_failed): CCSService.request_failed.connect(_on_request_failed) func on_show(_payload: Variant = null) -> void: pending_action = "" pending_attempt_id = "" status_label.text = "Loading case history..." load_more_button.disabled = true CCSService.load_cases() CCSService.load_active() CCSService.load_prediction() CCSService.load_rating() _load_history_page() func refresh_theme_controls() -> void: if page == null: return var dark := UIThemeService.is_dark_mode() active_section.add_theme_stylebox_override("panel", _section_box(dark)) history_section.add_theme_stylebox_override("panel", _section_box(dark)) status_label.add_theme_color_override("font_color", Brand.muted_color(dark)) _style_option(filter_option, dark) _style_option(sort_option, dark) _style_outline_button(load_more_button, Brand.PURPLE, dark) _render_active_section() _render_history() func _render_active_section() -> void: if active_content == null: return _clear(active_content) var dark := UIThemeService.is_dark_mode() var has_active := has_active_case(CCSService.attempt) var section_title := Label.new() section_title.text = "Active Case" if has_active else "Timed CCS Case" Brand.apply_font(section_title) section_title.add_theme_font_size_override("font_size", 18) section_title.add_theme_color_override("font_color", Color("9b73ff") if dark else Brand.PURPLE) active_content.add_child(section_title) var card := PanelContainer.new() card.add_theme_stylebox_override("panel", _active_card_box(dark)) active_content.add_child(card) var card_layout := BoxContainer.new() card_layout.name = "ActiveCardLayout" card_layout.vertical = compact_layout card_layout.add_theme_constant_override("separation", 18) card.add_child(card_layout) var icon_panel := PanelContainer.new() icon_panel.custom_minimum_size = Vector2(94, 94) icon_panel.size_flags_vertical = Control.SIZE_SHRINK_CENTER icon_panel.add_theme_stylebox_override("panel", Brand.icon_circle_box(dark, Brand.TEAL, 8)) card_layout.add_child(icon_panel) var icon := TextureRect.new() icon.texture = CASE_ICON icon.custom_minimum_size = Vector2(44, 44) icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED icon.modulate = Brand.TEAL icon_panel.add_child(icon) var details := VBoxContainer.new() details.size_flags_horizontal = Control.SIZE_EXPAND_FILL details.add_theme_constant_override("separation", 5) card_layout.add_child(details) var heading := Label.new() heading.text = "Timed CCS Case" Brand.apply_font(heading) heading.add_theme_font_size_override("font_size", 19) heading.add_theme_color_override("font_color", Brand.text_color(dark)) details.add_child(heading) if has_active: _build_active_details(details, dark) else: _build_start_details(details, dark) var actions := VBoxContainer.new() actions.name = "ActiveActions" if not compact_layout: actions.custom_minimum_size = Vector2(265, 0) actions.size_flags_vertical = Control.SIZE_SHRINK_CENTER actions.add_theme_constant_override("separation", 10) card_layout.add_child(actions) var primary := Button.new() primary.name = "PrimaryCcsAction" primary.text = "Resume Case" if has_active else "Start Case" primary.custom_minimum_size = Vector2(0, 48) primary.disabled = not pending_action.is_empty() or (not has_active and CCSService.cases.is_empty()) _style_gradient_button(primary) primary.pressed.connect(_launch_or_resume) actions.add_child(primary) if has_active: var abandon := Button.new() abandon.name = "AbandonCase" abandon.text = "Abandon Case" abandon.custom_minimum_size = Vector2(0, 46) _style_outline_button(abandon, Color("ff5f76"), dark) abandon.pressed.connect(func(): abandon_dialog.popup_centered(Vector2i(520, 220))) actions.add_child(abandon) _build_performance_summary(dark) var notice := PanelContainer.new() notice.add_theme_stylebox_override("panel", _notice_box(dark)) active_content.add_child(notice) var notice_label := Label.new() notice_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART notice_label.text = "Abandoning ends this case without a score; the attempt remains in case history." if has_active else _availability_message() _style_muted_label(notice_label, dark, 14) notice.add_child(notice_label) func _build_active_details(details: VBoxContainer, dark: bool) -> void: var attempt: Dictionary = CCSService.attempt var started := Label.new() started.text = "Started: %s" % _format_local_datetime(str(attempt.get("createdUtc", ""))) _style_muted_label(started, dark, 14) details.add_child(started) var state := Label.new() var state_text := "Final orders" if str(attempt.get("state", "")) == "final_orders" else "In progress" state.text = "%s | %s | %s" % [state_text, humanize(str(attempt.get("location", ""))), remaining_time_text(attempt)] _style_muted_label(state, dark, 14) details.add_child(state) func _build_start_details(details: VBoxContainer, dark: bool) -> void: var copy := Label.new() copy.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART copy.text = "Manage a randomly selected patient through focused examination, diagnostic orders, treatment, disposition, and simulated time." _style_muted_label(copy, dark, 15) details.add_child(copy) func _build_performance_summary(dark: bool) -> void: var panel := PanelContainer.new() panel.add_theme_stylebox_override("panel", _notice_box(dark)) active_content.add_child(panel) var row := BoxContainer.new() row.vertical = compact_layout row.add_theme_constant_override("separation", 18) panel.add_child(row) var rating_value := "%d" % int(CCSService.rating.get("elo", 1000)) var rating_detail := "%d rated cases" % int(CCSService.rating.get("ratedCaseCount", 0)) _add_metric(row, "CCS Rating", rating_value, rating_detail, dark) var ccs_score: Variant = CCSService.prediction.get("ccsScore", null) var score_value := "--" if ccs_score != null: score_value = "%d%%" % int(round(float(ccs_score))) _add_metric(row, "CCS Performance", score_value, "%d contributing cases" % int(CCSService.prediction.get("contributingCases", 0)), dark) var combined: Variant = CCSService.prediction.get("combinedPredictedScore", null) var prediction_value := "--" var prediction_detail := "Complete a Day 1 simulation" if combined != null: prediction_value = "%d" % int(combined) prediction_detail = "Range %d-%d" % [int(CCSService.prediction.get("rangeLow", 0)), int(CCSService.prediction.get("rangeHigh", 0))] _add_metric(row, "Combined Prediction", prediction_value, prediction_detail, dark) func _add_metric(parent: BoxContainer, caption: String, value: String, detail: String, dark: bool) -> void: var column := VBoxContainer.new() column.size_flags_horizontal = Control.SIZE_EXPAND_FILL parent.add_child(column) var caption_label := Label.new() caption_label.text = caption _style_muted_label(caption_label, dark, 12) column.add_child(caption_label) var value_label := Label.new() value_label.text = value Brand.apply_font(value_label) value_label.add_theme_font_size_override("font_size", 22) value_label.add_theme_color_override("font_color", Brand.text_color(dark)) column.add_child(value_label) var detail_label := Label.new() detail_label.text = detail _style_muted_label(detail_label, dark, 12) column.add_child(detail_label) func _render_history() -> void: if history_list == null or not visible: return _clear(history_list) var items: Array = CCSService.history status_label.text = "No cases match this filter." if items.is_empty() else "" for raw_item in items: var item: Dictionary = raw_item history_list.add_child(_build_history_row(item)) load_more_button.visible = not CCSService.history_next_cursor.is_empty() load_more_button.disabled = false table_header.visible = not compact_layout and not items.is_empty() func _build_table_header() -> HBoxContainer: var dark := UIThemeService.is_dark_mode() var row := HBoxContainer.new() row.add_theme_constant_override("separation", 10) for definition in [["Case", 2.0], ["Date", 1.15], ["Status", 1.0], ["Score", 0.8], ["CCS Rating", 1.0], ["Actions", 1.0]]: var label := Label.new() label.text = str(definition[0]) label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.size_flags_stretch_ratio = float(definition[1]) Brand.apply_font(label) label.add_theme_font_size_override("font_size", 13) label.add_theme_color_override("font_color", Brand.muted_color(dark)) row.add_child(label) return row func _build_history_row(item: Dictionary) -> Control: var dark := UIThemeService.is_dark_mode() var panel := PanelContainer.new() panel.add_theme_stylebox_override("panel", _row_box(dark)) var state := str(item.get("state", "")) if compact_layout: var card := VBoxContainer.new() card.add_theme_constant_override("separation", 8) panel.add_child(card) var top := HBoxContainer.new() card.add_child(top) top.add_child(_case_cell(item, dark)) top.add_child(_status_badge(state, dark)) var metrics := Label.new() metrics.text = "%s | %s | %s" % [_format_local_datetime(str(item.get("createdUtc", ""))).replace("\n", " "), _score_text(item), _rating_text(item)] _style_muted_label(metrics, dark, 14) card.add_child(metrics) if state == "completed": card.add_child(_review_button(item, dark)) return panel var row := HBoxContainer.new() row.custom_minimum_size = Vector2(0, 66) row.add_theme_constant_override("separation", 10) panel.add_child(row) _add_table_cell(row, _case_cell(item, dark), 2.0) _add_table_cell(row, _plain_cell(_format_local_datetime(str(item.get("createdUtc", ""))), dark), 1.15) _add_table_cell(row, _status_badge(state, dark), 1.0) _add_table_cell(row, _plain_cell(_score_text(item), dark), 0.8) _add_table_cell(row, _plain_cell(_rating_text(item), dark), 1.0) var action: Control = _plain_cell("-", dark) if state == "completed": action = _review_button(item, dark) _add_table_cell(row, action, 1.0) return panel func _case_cell(item: Dictionary, dark: bool) -> Control: var column := VBoxContainer.new() column.size_flags_horizontal = Control.SIZE_EXPAND_FILL var title := Label.new() title.text = str(item.get("caseTitle", "CCS Case")) title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART Brand.apply_font(title) title.add_theme_font_size_override("font_size", 15) title.add_theme_color_override("font_color", Brand.text_color(dark)) column.add_child(title) var mode := Label.new() mode.text = "Timed case" _style_muted_label(mode, dark, 12) column.add_child(mode) return column func _plain_cell(value: String, dark: bool) -> Label: var label := Label.new() label.text = value label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART Brand.apply_font(label) label.add_theme_font_size_override("font_size", 14) label.add_theme_color_override("font_color", Brand.text_color(dark)) return label func _status_badge(state: String, dark: bool) -> Label: var label := Label.new() var accent := Brand.SUCCESS if state == "completed" else Brand.SLATE label.text = "Completed" if state == "completed" else "Abandoned" label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.size_flags_vertical = Control.SIZE_SHRINK_CENTER Brand.apply_font(label) label.add_theme_font_size_override("font_size", 12) label.add_theme_color_override("font_color", accent if dark else accent.darkened(0.2)) label.add_theme_stylebox_override("normal", Brand.badge_box(dark, accent)) return label func _review_button(item: Dictionary, dark: bool) -> Button: var button := Button.new() button.text = "Review" button.icon = REVIEW_ICON button.expand_icon = true button.add_theme_constant_override("icon_max_width", 17) button.custom_minimum_size = Vector2(105, 38) button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN _style_outline_button(button, Brand.PURPLE, dark) button.pressed.connect(_open_review.bind(str(item.get("id", "")))) return button func _add_table_cell(row: HBoxContainer, control: Control, ratio: float) -> void: control.size_flags_horizontal = Control.SIZE_EXPAND_FILL control.size_flags_vertical = Control.SIZE_SHRINK_CENTER control.size_flags_stretch_ratio = ratio row.add_child(control) func _launch_or_resume() -> void: if not pending_action.is_empty(): return pending_action = "resume" if has_active_case(CCSService.attempt) else "start" _render_active_section() if pending_action == "resume": main.show_screen("ccs_player") pending_action = "" else: CCSService.create_attempt("", "timed") func _open_review(attempt_id: String) -> void: if not pending_action.is_empty(): return pending_action = "review" pending_attempt_id = attempt_id status_label.text = "Opening case review..." CCSService.load_attempt(attempt_id) func _confirm_abandon() -> void: if not has_active_case(CCSService.attempt): return pending_action = "abandon" CCSService.abandon() _render_active_section() func _on_attempt_changed() -> void: if not visible: return if pending_action in ["start", "resume_after_conflict"] and has_active_case(CCSService.attempt): pending_action = "" main.show_screen("ccs_player") return if pending_action == "review" and str(CCSService.attempt.get("id", "")) == pending_attempt_id: pending_action = "" pending_attempt_id = "" main.show_screen("ccs_review", CCSService.attempt) return pending_action = "" _render_active_section() func _on_attempt_abandoned() -> void: if not visible: return pending_action = "" CCSService.load_active() CCSService.load_prediction() CCSService.load_rating() _load_history_page() func _on_filter_selected(index: int) -> void: history_status = str(filter_option.get_item_metadata(index)) _load_history_page() func _on_sort_selected(index: int) -> void: history_sort = str(sort_option.get_item_metadata(index)) _load_history_page() func _load_history_page() -> void: status_label.text = "Loading case history..." load_more_button.disabled = true CCSService.load_history("", history_status, history_sort) func _load_more() -> void: if CCSService.history_next_cursor.is_empty(): return load_more_button.disabled = true CCSService.load_history(CCSService.history_next_cursor, history_status, history_sort) func _on_request_failed(message: String) -> void: if not visible: return if pending_action == "start" and message.contains("409"): pending_action = "resume_after_conflict" status_label.text = "An active case was found. Resuming it..." CCSService.load_active() return pending_action = "" pending_attempt_id = "" status_label.text = message load_more_button.disabled = false _render_active_section() func _update_responsive_layout() -> void: var use_compact := size.x < 940 if compact_layout == use_compact: return compact_layout = use_compact history_header.vertical = compact_layout filter_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL if compact_layout else Control.SIZE_SHRINK_END sort_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL if compact_layout else Control.SIZE_SHRINK_END _render_active_section() _render_history() func _availability_message() -> String: if CCSService.cases.is_empty(): return "No published CCS cases are currently available." return "%d published cases are available. A random case will begin immediately." % CCSService.cases.size() func _score_text(item: Dictionary) -> String: var score: Variant = item.get("score", null) return "--" if score == null else "%d%%" % int(round(float(score))) func _rating_text(item: Dictionary) -> String: var rating_value: Variant = item.get("rating", null) if typeof(rating_value) != TYPE_DICTIONARY: return "--" var rating: Dictionary = rating_value if not bool(rating.get("rated", false)): return "--" return "%d (%+d)" % [int(rating.get("userElo", 1000)), int(rating.get("userEloDelta", 0))] func _format_local_datetime(value: String) -> String: if value.is_empty(): return "--" var unix := Time.get_unix_time_from_datetime_string(value) var timezone: Dictionary = Time.get_time_zone_from_system() var local := Time.get_datetime_dict_from_unix_time(int(unix) + int(timezone.get("bias", 0)) * 60) return format_datetime_parts(local) static func format_datetime_parts(parts: Dictionary) -> String: const MONTHS := ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] var month_index: int = clamp(int(parts.get("month", 1)) - 1, 0, 11) var hour_24 := int(parts.get("hour", 0)) var suffix := "AM" if hour_24 < 12 else "PM" var hour_12 := hour_24 % 12 if hour_12 == 0: hour_12 = 12 return "%s %d, %d\n%d:%02d %s" % [MONTHS[month_index], int(parts.get("day", 1)), int(parts.get("year", 1970)), hour_12, int(parts.get("minute", 0)), suffix] static func has_active_case(attempt: Dictionary) -> bool: return not attempt.is_empty() and str(attempt.get("state", "")) in ["active", "final_orders"] static func remaining_time_text(attempt: Dictionary, now_unix: float = -1.0) -> String: var deadline := str(attempt.get("finalOrdersDeadlineUtc", "")) if str(attempt.get("state", "")) == "final_orders" else str(attempt.get("deadlineUtc", "")) if deadline.is_empty() or deadline == "": return "Timed" var current_unix := Time.get_unix_time_from_system() if now_unix < 0.0 else now_unix var seconds: int = max(0, int(Time.get_unix_time_from_datetime_string(deadline) - current_unix)) var minutes := int(ceil(float(seconds) / 60.0)) return "%d min remaining" % minutes static func humanize(value: String) -> String: return value.replace("_", " ").capitalize() func _style_muted_label(label: Label, dark: bool, font_size: int) -> void: Brand.apply_font(label) label.add_theme_font_size_override("font_size", font_size) label.add_theme_color_override("font_color", Brand.muted_color(dark)) func _style_option(option: OptionButton, dark: bool) -> void: Brand.apply_font(option) option.add_theme_font_size_override("font_size", 14) option.add_theme_color_override("font_color", Brand.text_color(dark)) for state in ["normal", "hover", "pressed"]: option.add_theme_stylebox_override(state, Brand.secondary_button_box(dark, state != "normal")) func _style_gradient_button(button: Button) -> void: Brand.apply_font(button) button.add_theme_font_size_override("font_size", 15) for state in ["normal", "hover", "pressed"]: button.add_theme_stylebox_override(state, Brand.gradient_button_box()) button.add_theme_color_override("font_color", Color.WHITE) button.add_theme_color_override("font_hover_color", Color.WHITE) func _style_outline_button(button: Button, accent: Color, dark: bool) -> void: Brand.apply_font(button) button.add_theme_font_size_override("font_size", 14) var normal := Style.button_box(Color(0, 0, 0, 0), accent) var hover := Style.button_box(Color(accent, 0.12), accent) button.add_theme_stylebox_override("normal", normal) button.add_theme_stylebox_override("hover", hover) button.add_theme_stylebox_override("pressed", hover) button.add_theme_color_override("font_color", accent if dark else accent.darkened(0.08)) button.add_theme_color_override("font_hover_color", accent) button.add_theme_color_override("icon_normal_color", accent) func _section_box(dark: bool) -> StyleBoxFlat: var box := Brand.content_card_box(dark, false) box.content_margin_left = 16 box.content_margin_right = 16 box.content_margin_top = 14 box.content_margin_bottom = 14 return box func _active_card_box(dark: bool) -> StyleBoxFlat: var box := Brand.content_card_box(dark, true, Brand.TEAL) box.bg_color = Color("0c1c31") if dark else Color("f4fbfb") box.border_color = Color("174c58") if dark else Color("b9e7e2") box.content_margin_left = 16 box.content_margin_right = 16 box.content_margin_top = 16 box.content_margin_bottom = 16 return box func _notice_box(dark: bool) -> StyleBoxFlat: var box := StyleBoxFlat.new() box.bg_color = Color("101a32") if dark else Color("f5f7ff") box.border_color = Color("29365d") if dark else Color("dbe3f4") box.set_border_width_all(1) box.set_corner_radius_all(7) box.content_margin_left = 12 box.content_margin_right = 12 box.content_margin_top = 9 box.content_margin_bottom = 9 return box func _row_box(dark: bool) -> StyleBoxFlat: var box := StyleBoxFlat.new() box.bg_color = Color("0c162b") if dark else Color("ffffff") box.border_color = Color("273450") if dark else Color("e3e8f1") box.border_width_top = 1 box.content_margin_left = 12 box.content_margin_right = 12 box.content_margin_top = 10 box.content_margin_bottom = 10 return box func _clear(container: Node) -> void: for child in container.get_children(): container.remove_child(child) child.queue_free()