extends VBoxContainer const Style := preload("res://scripts/ui/ui_style.gd") var main: Control var heading: Label var clock: Label var summary: RichTextLabel var chart: RichTextLabel var orders: VBoxContainer var search: LineEdit var search_results: VBoxContainer var actions: VBoxContainer var status: Label var timer: Timer var end_button: Button var time_row: HBoxContainer var report_dialog: Control var report_button: Button var app_feedback_button: Button var workspace: HSplitContainer var leave_button: Button var leaving := false var theme_service var theme_button: Button func _ready() -> void: theme_service = get_node("/root/UIThemeService") set_anchors_preset(Control.PRESET_FULL_RECT); add_theme_constant_override("separation", 8) var top := HBoxContainer.new(); top.add_theme_constant_override("separation", 8); add_child(top) heading = Label.new(); heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL; Style.set_label(heading, 25, Style.TEXT); top.add_child(heading) clock = Label.new(); Style.set_label(clock, 18, Style.AMBER); top.add_child(clock) var smaller := Button.new(); smaller.text = "A-"; smaller.tooltip_text = "Decrease text size"; smaller.pressed.connect(_zoom.bind(-CCSService.TEXT_SCALE_STEP)); top.add_child(smaller) var larger := Button.new(); larger.text = "A+"; larger.tooltip_text = "Increase text size"; larger.pressed.connect(_zoom.bind(CCSService.TEXT_SCALE_STEP)); top.add_child(larger) var clear_highlights := Button.new(); clear_highlights.text = "Clear"; clear_highlights.tooltip_text = "Clear highlights"; clear_highlights.pressed.connect(_clear_highlights); top.add_child(clear_highlights) report_button = Button.new(); report_button.text = "Report"; report_button.tooltip_text = "Report CCS case"; report_button.pressed.connect(_report); top.add_child(report_button) app_feedback_button = Button.new(); app_feedback_button.text = "Feedback"; app_feedback_button.tooltip_text = "Send general feedback or report a bug"; app_feedback_button.pressed.connect(func(): main.open_feedback_dialog()); top.add_child(app_feedback_button) theme_button = Button.new(); theme_button.custom_minimum_size = Vector2(44, 44); theme_button.add_theme_font_size_override("font_size", 22); UISoundService.set_button_sound(theme_button, "toggle"); theme_button.pressed.connect(theme_service.toggle_theme); top.add_child(theme_button); _refresh_theme_button() workspace = HSplitContainer.new(); workspace.size_flags_vertical = Control.SIZE_EXPAND_FILL; workspace.split_offset = 850; add_child(workspace) var chart_scroll := ScrollContainer.new(); chart_scroll.custom_minimum_size.x = 500; chart_scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL; workspace.add_child(chart_scroll) var chart_panel := VBoxContainer.new(); chart_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL; chart_panel.add_theme_constant_override("separation", 18); chart_scroll.add_child(chart_panel) summary = RichTextLabel.new(); summary.bbcode_enabled = true; summary.selection_enabled = true; summary.fit_content = true; summary.scroll_active = false; summary.set_meta("ccs_region", "summary"); summary.gui_input.connect(_text_input.bind(summary)); chart_panel.add_child(summary) chart = RichTextLabel.new(); chart.bbcode_enabled = true; chart.selection_enabled = true; chart.fit_content = true; chart.scroll_active = false; chart.set_meta("ccs_region", "chart"); chart.gui_input.connect(_text_input.bind(chart)); chart_panel.add_child(chart) var clerk_scroll := ScrollContainer.new(); clerk_scroll.custom_minimum_size.x = 430; workspace.add_child(clerk_scroll) var clerk := VBoxContainer.new(); clerk.size_flags_horizontal = Control.SIZE_EXPAND_FILL; clerk.add_theme_constant_override("separation", 8); clerk_scroll.add_child(clerk) var order_title := Label.new(); order_title.text = "Order Clerk"; Style.set_label(order_title, 21, Style.CYAN); clerk.add_child(order_title) search = LineEdit.new(); search.placeholder_text = "Search tests, treatments, and procedures"; search.text_submitted.connect(func(_value): CCSService.search_orders(search.text)); clerk.add_child(search) var find := Button.new(); find.text = "Search"; find.pressed.connect(func(): CCSService.search_orders(search.text)); clerk.add_child(find) search_results = VBoxContainer.new(); clerk.add_child(search_results) var active_title := Label.new(); active_title.text = "Active orders"; Style.set_label(active_title, 18, Style.TEXT); clerk.add_child(active_title) orders = VBoxContainer.new(); clerk.add_child(orders) var exam_title := Label.new(); exam_title.text = "Physical examination"; Style.set_label(exam_title, 18, Style.TEXT); clerk.add_child(exam_title) actions = VBoxContainer.new(); clerk.add_child(actions) time_row = HBoxContainer.new(); clerk.add_child(time_row) for minutes in [1, 10, 60, 1440]: var button := Button.new(); button.text = "+%s" % ("1 day" if minutes == 1440 else "%d min" % minutes); button.pressed.connect(CCSService.send_command.bind("advance", "", minutes)); time_row.add_child(button) var reassess := Button.new(); reassess.text = "Interval history"; reassess.pressed.connect(CCSService.send_command.bind("interval_history", "", 0)); clerk.add_child(reassess) end_button = Button.new(); end_button.text = "End case"; Style.style_chip(end_button, Style.AMBER); end_button.pressed.connect(_end_case); clerk.add_child(end_button) leave_button = Button.new(); leave_button.text = "Leave case"; Style.style_chip(leave_button, Style.RED); leave_button.pressed.connect(_leave); clerk.add_child(leave_button) status = Label.new(); Style.set_label(status, 14, Style.RED); add_child(status) timer = Timer.new(); timer.wait_time = 1; timer.timeout.connect(_tick); add_child(timer) CCSService.attempt_changed.connect(_render) CCSService.attempt_abandoned.connect(_on_abandoned) CCSService.orders_changed.connect(_render_search) CCSService.request_failed.connect(_request_failed) CCSService.report_submitted.connect(_report_submitted) if not theme_service.theme_changed.is_connected(_on_theme_changed): theme_service.theme_changed.connect(_on_theme_changed) report_dialog = preload("res://scripts/ui/ccs_report_dialog.gd").new(); add_child(report_dialog) func on_show(payload: Variant = null) -> void: status.text = "" leaving = false leave_button.disabled = false if typeof(payload) == TYPE_STRING and not str(payload).is_empty(): CCSService.load_attempt(str(payload)) _render(); timer.start() func on_hide() -> void: timer.stop() func _render() -> void: var attempt: Dictionary = CCSService.attempt if attempt.is_empty(): return if str(attempt.get("state", "")) == "completed": main.show_screen("ccs_review", attempt); return heading.text = "CCS Case | %s" % str(attempt.get("location", "")).capitalize() _render_study_text() _clear(orders) for code in attempt.get("activeOrderCodes", []): var row := HBoxContainer.new(); orders.add_child(row) var label := Label.new(); label.text = str(code); label.size_flags_horizontal = Control.SIZE_EXPAND_FILL; row.add_child(label) var cancel := Button.new(); cancel.text = "Cancel"; cancel.pressed.connect(CCSService.send_command.bind("cancel_order", str(code), 0)); row.add_child(cancel) _clear(actions) for exam in attempt.get("availableExams", []): var button := Button.new(); button.text = str(exam.get("name", exam.get("code", "Exam"))); button.pressed.connect(CCSService.send_command.bind("exam", str(exam.get("code", "")), 0)); actions.add_child(button) for location in attempt.get("allowedLocations", []): var button := Button.new(); button.text = "Move to %s" % str(location).capitalize(); button.pressed.connect(CCSService.send_command.bind("location", str(location), 0)); actions.add_child(button) _tick() end_button.text = "Submit final orders" if str(attempt.get("state", "")) == "final_orders" else "End case" time_row.visible = str(attempt.get("state", "")) == "active" if str(attempt.get("state", "")) == "final_orders": status.text = "Case-end instructions: finalize or cancel orders, then submit." _apply_text_scale() func _render_search() -> void: _clear(search_results) for order in CCSService.orders: var button := Button.new(); button.text = "%s (%s)" % [order.get("canonicalName", ""), order.get("category", "")]; button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.pressed.connect(_place_order.bind(str(order.get("code", "")))); search_results.add_child(button) _scale_controls(search_results) func _place_order(code: String) -> void: CCSService.send_command("order", code); search.text = ""; CCSService.orders = []; _render_search() func _tick() -> void: var deadline := CCSService.deadline_unix() if deadline <= 0: clock.visible = false clock.text = "" return clock.visible = true var remaining := maxi(0, int(ceil(deadline - Time.get_unix_time_from_system()))) clock.text = "%02d:%02d" % [floori(float(remaining) / 60.0), remaining % 60] if remaining == 0 and not CCSService.is_loading: CCSService.load_attempt(str(CCSService.attempt.get("id", ""))) func leave_case() -> void: _leave() func _leave() -> void: if leaving or CCSService.attempt.is_empty(): return leaving = true leave_button.disabled = true status.text = "Leaving case..." CCSService.abandon() func _on_abandoned() -> void: if not leaving: return leaving = false main.show_screen("selection", "step3") func _request_failed(message: String) -> void: if not visible: return status.text = message if leaving: leaving = false leave_button.disabled = false func _end_case() -> void: CCSService.send_command("exit_case" if str(CCSService.attempt.get("state", "")) == "final_orders" else "end_case") func _clear(parent: Node) -> void: for child in parent.get_children(): child.queue_free() func _vitals_text(vitals: Dictionary) -> String: var parts: Array[String] = [] for key in vitals: parts.append("%s: %s" % [str(key), str(vitals[key])]) return " | ".join(parts) func _chart_text(entries: Array) -> String: var lines: Array[String] = [] for entry in entries: lines.append("T+%d [%s] %s" % [entry.get("simulatedMinute", 0), entry.get("category", "note"), entry.get("text", "")]) return "\n\n".join(lines) func _summary_plain() -> String: var attempt := CCSService.attempt return "%s\n%s\n\nVitals %s" % [attempt.get("patientSummary", ""), attempt.get("reasonForEncounter", ""), _vitals_text(attempt.get("vitals", {}))] func _chart_plain() -> String: var attempt := CCSService.attempt return "History\n%s\n\nAppearance\n%s\n\nChart\n%s" % [attempt.get("history", ""), attempt.get("appearance", ""), _chart_text(attempt.get("chart", []))] func _render_study_text() -> void: var id := str(CCSService.attempt.get("id", "")) summary.text = CCSService.highlighted_text(id, "summary", _summary_plain()) chart.text = CCSService.highlighted_text(id, "chart", _chart_plain()) func _text_input(event: InputEvent, label: RichTextLabel) -> void: var mouse := event as InputEventMouseButton if mouse != null and mouse.button_index == MOUSE_BUTTON_LEFT and not mouse.pressed: call_deferred("_commit_selection", label) func _commit_selection(label: RichTextLabel) -> void: var selected := label.get_selected_text().strip_edges() if selected.is_empty(): return var start := label.get_selection_from(); var end := label.get_selection_to() CCSService.toggle_highlight(str(CCSService.attempt.get("id", "")), str(label.get_meta("ccs_region", "")), mini(start, end), maxi(start, end)) label.deselect(); _render_study_text() func _zoom(delta: float) -> void: CCSService.adjust_text_scale(delta); _apply_text_scale() func _apply_text_scale() -> void: var font_size := int(round(18.0 * CCSService.text_scale)) for label in [summary, chart]: label.add_theme_font_size_override("normal_font_size", font_size); label.add_theme_font_size_override("bold_font_size", font_size) _scale_controls(workspace) func _scale_controls(node: Node) -> void: if node is Label or node is Button or node is LineEdit: if not node.has_meta("ccs_base_font_size"): node.set_meta("ccs_base_font_size", node.get_theme_font_size("font_size")) node.add_theme_font_size_override("font_size", int(round(float(node.get_meta("ccs_base_font_size")) * CCSService.text_scale))) for child in node.get_children(): _scale_controls(child) func _clear_highlights() -> void: CCSService.clear_highlights(str(CCSService.attempt.get("id", ""))); _render_study_text() func _report() -> void: report_dialog.show_for(str(CCSService.attempt.get("id", ""))) func _report_submitted(_id: String) -> void: Style.style_chip(report_button, Style.RED); report_button.tooltip_text = "Report submitted; report another issue" func _on_theme_changed(_is_dark_mode: bool) -> void: _refresh_theme_button() func _refresh_theme_button() -> void: if theme_button == null: return theme_button.text = "☀" if theme_service.is_dark_mode() else "☾" theme_button.tooltip_text = "Switch to light mode" if theme_service.is_dark_mode() else "Switch to dark mode" Style.style_chip(theme_button, Style.CYAN)