Merge main into production

This commit is contained in:
hz
2026-07-30 17:08:31 -05:00
18 changed files with 1476 additions and 126 deletions
+20 -9
View File
@@ -562,7 +562,10 @@ func _ready() -> void:
var button := Button.new()
button.custom_minimum_size = Vector2(0, 68)
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
button.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
button.text_overrun_behavior = TextServer.OVERRUN_NO_TRIMMING
Style.style_answer_button(button)
UISoundService.set_button_sound(button, "select")
_set_scaled_button_font(button, 17)
@@ -672,6 +675,7 @@ func _ready() -> void:
actions = HBoxContainer.new()
actions.add_theme_constant_override("separation", 16)
actions.size_flags_horizontal = Control.SIZE_EXPAND_FILL
card_content.add_child(actions)
skip_button = Button.new()
@@ -701,10 +705,6 @@ func _ready() -> void:
bottom_progress_bar.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
bottom_progress_group.add_child(bottom_progress_bar)
# The timer readout still runs (BlockSessionService pacing/scoring depends on
# it, and _update_timer_bar keeps its text current), it's just no longer
# parented into the bottom bar - the caption + digits and the "Ctrl+Enter"
# hint were clutter next to the Submit button.
timer_caption_label = Label.new()
timer_caption_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
_set_scaled_label(timer_caption_label, 12, Brand.SLATE)
@@ -713,6 +713,7 @@ func _ready() -> void:
next_button.text = "Submit Block" if BlockSessionService.session_kind == "simulation" and BlockSessionService.current_index + 1 == BlockSessionService.active_questions.size() else "Next" if BlockSessionService.session_kind == "simulation" else "Submit"
next_button.tooltip_text = ""
next_button.custom_minimum_size = Vector2(140, 48)
next_button.size_flags_horizontal = Control.SIZE_SHRINK_END
Style.style_primary_button(next_button)
next_button.pressed.connect(_submit_or_next)
actions.add_child(next_button)
@@ -720,6 +721,8 @@ func _ready() -> void:
timer_footer = HBoxContainer.new()
timer_footer.add_theme_constant_override("separation", 12)
card_content.add_child(timer_footer)
timer_footer.add_child(timer_caption_label)
timer_footer.add_child(timer_label)
timer_bar = ProgressBar.new()
timer_bar.show_percentage = false
timer_bar.min_value = 0.0
@@ -1160,19 +1163,22 @@ func on_hide() -> void:
slot_machine_panel.close_panel()
func _save_simulation_current_response() -> void:
func _save_simulation_current_response(is_submitted := false) -> void:
if BlockSessionService.session_kind != "simulation":
return
if bool(current_answer.get("is_submitted", false)) and not is_submitted:
return
var question := BlockSessionService.get_current_question()
if question.is_empty():
return
current_answer = BlockSessionService.answer_current(selected_choice, eliminated, flagged)
current_answer = BlockSessionService.answer_current(selected_choice, eliminated, flagged, is_submitted)
ExamSimulationService.save_response(
str(question.get("id", "")),
selected_choice,
eliminated,
flagged,
int(BlockSessionService.get_current_elapsed_seconds()))
int(BlockSessionService.get_current_elapsed_seconds()),
is_submitted)
func _on_simulation_state_changed() -> void:
@@ -1192,6 +1198,9 @@ func _on_simulation_state_changed() -> void:
func _on_simulation_request_failed(message: String) -> void:
if BlockSessionService.session_kind != "simulation":
return
if str(ExamSimulationService.simulation.get("state", "")) in ["between_blocks", "completed"]:
simulation_submit_pending = false
return
simulation_submit_pending = false
if ExamSimulationService.last_failed_action != "submit":
# A per-question autosave failing shouldn't interrupt a timed exam with a
@@ -1582,7 +1591,7 @@ func _submit_or_next() -> void:
if BlockSessionService.session_kind == "simulation":
if selected_choice == "":
return
_save_simulation_current_response()
_save_simulation_current_response(true)
var pending_index := _next_pending_simulation_index()
if pending_index >= 0:
BlockSessionService.move_to(pending_index)
@@ -3385,7 +3394,9 @@ func _apply_saved_choice_state() -> void:
func _is_correct_answer_locked() -> bool:
return bool(current_answer.get("is_correct", false))
return bool(current_answer.get("is_correct", false)) or (
BlockSessionService.session_kind == "simulation" and
bool(current_answer.get("is_submitted", false)))
func _refresh_submit_state() -> void: