From 1835702daaf7216d9350587b76bb0b084a1a2b1a Mon Sep 17 00:00:00 2001 From: Aidan <76609332+redbaron2k7@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:34:44 -0500 Subject: [PATCH 1/3] add support for websockets with messages to network tab --- flumi/Scenes/DevTools.tscn | 5 + flumi/Scripts/Browser/NetworkManager.gd | 52 +++++- flumi/Scripts/Browser/NetworkRequest.gd | 78 +++++++- flumi/Scripts/Browser/NetworkTab.gd | 239 +++++++++++++++++++++++- flumi/Scripts/Utils/Lua/WebSocket.gd | 20 ++ 5 files changed, 389 insertions(+), 5 deletions(-) diff --git a/flumi/Scenes/DevTools.tscn b/flumi/Scenes/DevTools.tscn index ace2907..fb2401e 100644 --- a/flumi/Scenes/DevTools.tscn +++ b/flumi/Scenes/DevTools.tscn @@ -489,6 +489,11 @@ visible = false layout_mode = 2 metadata/_tab_index = 2 +[node name="Messages" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"] +visible = false +layout_mode = 2 +metadata/_tab_index = 3 + [node name="Application" type="Label" parent="DevTools/TabContainer"] visible = false layout_mode = 2 diff --git a/flumi/Scripts/Browser/NetworkManager.gd b/flumi/Scripts/Browser/NetworkManager.gd index bc970b0..4316012 100644 --- a/flumi/Scripts/Browser/NetworkManager.gd +++ b/flumi/Scripts/Browser/NetworkManager.gd @@ -138,4 +138,54 @@ func add_completed_request(url: String, method: String, is_from_lua: bool, statu all_requests.append(request) if dev_tools_network_tab: - dev_tools_network_tab.add_network_request(request) \ No newline at end of file + dev_tools_network_tab.add_network_request(request) + +func start_websocket_connection(url: String, websocket_id: String) -> NetworkRequest: + var request = NetworkRequest.create_websocket_connection(url, websocket_id) + active_requests[request.id] = request + all_requests.append(request) + + if dev_tools_network_tab: + dev_tools_network_tab.add_network_request(request) + + request_started.emit(request) + return request + +func add_websocket_message(url: String, websocket_id: String, direction: String, message: String): + var connection_request: NetworkRequest = null + + for request in active_requests.values(): + if request.websocket_id == websocket_id and request.websocket_event_type == "connection": + connection_request = request + break + + if not connection_request: + for request in all_requests: + if request.websocket_id == websocket_id and request.websocket_event_type == "connection": + connection_request = request + break + + if connection_request: + connection_request.add_websocket_message(direction, message) + + if dev_tools_network_tab: + dev_tools_network_tab.update_request_item(connection_request) + + request_completed.emit(connection_request) + +func update_websocket_connection(websocket_id: String, status: String, status_code: int = 200, status_text: String = "OK"): + for request in active_requests.values(): + if request.websocket_id == websocket_id and request.websocket_event_type == "connection": + request.update_websocket_status(status, status_code, status_text) + + if status in ["closed", "error"]: + active_requests.erase(request.id) + + if dev_tools_network_tab: + dev_tools_network_tab.update_request_item(request) + + if request.status == NetworkRequest.RequestStatus.SUCCESS: + request_completed.emit(request) + else: + request_failed.emit(request) + break \ No newline at end of file diff --git a/flumi/Scripts/Browser/NetworkRequest.gd b/flumi/Scripts/Browser/NetworkRequest.gd index 571b303..e09d3d1 100644 --- a/flumi/Scripts/Browser/NetworkRequest.gd +++ b/flumi/Scripts/Browser/NetworkRequest.gd @@ -42,6 +42,27 @@ var response_body_bytes: PackedByteArray = [] var mime_type: String = "" var is_from_lua: bool = false +var websocket_id: String = "" +var websocket_event_type: String = "" # "connection", "close", "error" +var connection_status: String = "" # "connecting", "open", "closing", "closed" +var websocket_messages: Array[WebSocketMessage] = [] + +class WebSocketMessage: + var timestamp: float + var direction: String # "sent" or "received" + var content: String + var size: int + + func _init(dir: String, msg: String): + timestamp = Time.get_ticks_msec() + direction = dir + content = msg + size = msg.length() + + func get_formatted_time() -> String: + var time_obj = Time.get_datetime_dict_from_unix_time(timestamp / 1000.0) + return "%02d:%02d:%02d.%03d" % [time_obj.hour, time_obj.minute, time_obj.second, int(timestamp) % 1000] + func _init(request_url: String = "", request_method: String = "GET"): id = generate_id() url = request_url @@ -62,6 +83,20 @@ func generate_id() -> String: func extract_name_from_url(request_url: String) -> String: if request_url.is_empty(): return "Unknown" + + if request_url.begins_with("ws://") or request_url.begins_with("wss://"): + if not websocket_event_type.is_empty(): + match websocket_event_type: + "connection": + var message_count = websocket_messages.size() + if message_count > 0: + return "WebSocket (" + str(message_count) + " messages)" + else: + return "WebSocket Connection" + "close": + return "WebSocket Close" + "error": + return "WebSocket Error" var parts = request_url.split("/") if parts.size() > 0: @@ -175,9 +210,9 @@ static func format_bytes(given_size: int) -> String: if given_size < 1024: return str(given_size) + " B" elif given_size < 1024 * 1024: - return str(given_size / 1024) + " KB" + return str(given_size / 1024.0) + " KB" else: - return str(given_size / (1024 * 1024)) + " MB" + return str(given_size / (1024.0 * 1024.0)) + " MB" func get_time_display() -> String: if status == RequestStatus.PENDING: @@ -205,3 +240,42 @@ func get_icon_texture() -> Texture2D: return load("res://Assets/Icons/arrow-down-up.svg") _: return load("res://Assets/Icons/search.svg") + +static func create_websocket_connection(ws_url: String, ws_id: String) -> NetworkRequest: + var request = NetworkRequest.new(ws_url, "WS") + request.type = RequestType.SOCKET + request.websocket_id = ws_id + request.websocket_event_type = "connection" + request.connection_status = "connecting" + request.is_from_lua = true + return request + +func add_websocket_message(direction: String, message: String): + var ws_message = WebSocketMessage.new(direction, message) + websocket_messages.append(ws_message) + + name = extract_name_from_url(url) + + var total_message_size = 0 + for msg in websocket_messages: + total_message_size += msg.size + size = total_message_size + +func update_websocket_status(new_status: String, status_code: int = 200, status_text: String = "OK"): + connection_status = new_status + self.status_code = status_code + self.status_text = status_text + + match new_status: + "open": + status = RequestStatus.SUCCESS + "closed": + if status_code >= 1000 and status_code < 1100: + status = RequestStatus.SUCCESS + else: + status = RequestStatus.ERROR + "error": + status = RequestStatus.ERROR + + end_time = Time.get_ticks_msec() + time_ms = end_time - start_time diff --git a/flumi/Scripts/Browser/NetworkTab.gd b/flumi/Scripts/Browser/NetworkTab.gd index bc15d84..983ea6e 100644 --- a/flumi/Scripts/Browser/NetworkTab.gd +++ b/flumi/Scripts/Browser/NetworkTab.gd @@ -11,6 +11,7 @@ const NetworkRequestItemScene = preload("res://Scenes/NetworkRequestItem.tscn") @onready var headers_tab: VBoxContainer = $MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer/Headers @onready var preview_tab: Control = $MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer/Preview @onready var response_tab: Control = $MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer/Response +@onready var messages_tab: Control = $MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer/Messages # Header components @onready var status_header: Label = %StatusHeader @@ -31,7 +32,7 @@ const NetworkRequestItemScene = preload("res://Scenes/NetworkRequestItem.tscn") @onready var syntax_highlighter = preload("res://Resources/LuaSyntaxHighlighter.tres") var network_requests: Array[NetworkRequest] = [] -var current_filter: NetworkRequest.RequestType = -1 # -1 means all +var current_filter: int = -1 # -1 means all, otherwise NetworkRequest.RequestType var selected_request: NetworkRequest = null var request_items: Dictionary = {} @@ -66,7 +67,7 @@ func apply_filter(): for request in network_requests: var item = request_items.get(request.id) if item: - var should_show = (current_filter == -1) or (request.type == current_filter) + var should_show = (current_filter == -1) or (int(request.type) == current_filter) item.visible = should_show func update_request_item(request: NetworkRequest): @@ -76,6 +77,9 @@ func update_request_item(request: NetworkRequest): request_item.update_display() + if selected_request == request and details_panel.visible: + update_details_panel(request) + apply_filter() update_status_bar() @@ -84,11 +88,19 @@ func update_details_panel(request: NetworkRequest): update_headers_tab(request) update_preview_tab(request) update_response_tab(request) + update_messages_tab(request) + + if request.type == NetworkRequest.RequestType.SOCKET: + messages_tab.visible = true + details_tab_container.set_tab_title(3, "Messages (" + str(request.websocket_messages.size()) + ")") + else: + messages_tab.visible = false func clear_details_panel(): for child in headers_tab.get_children(): child.queue_free() for child in preview_tab.get_children(): child.queue_free() for child in response_tab.get_children(): child.queue_free() + for child in messages_tab.get_children(): child.queue_free() func create_collapsible_section(title: String, expanded: bool = false) -> VBoxContainer: var section = VBoxContainer.new() @@ -154,6 +166,17 @@ func update_headers_tab(request: NetworkRequest): add_header_row(general_content, "Request Method:", request.method) add_header_row(general_content, "Status Code:", str(request.status_code) + " " + request.status_text) + # WebSocket information + if request.type == NetworkRequest.RequestType.SOCKET: + var ws_section = create_collapsible_section("WebSocket Information", true) + headers_tab.add_child(ws_section) + + var ws_content = ws_section.get_child(1) + add_header_row(ws_content, "WebSocket ID:", request.websocket_id) + add_header_row(ws_content, "Event Type:", request.websocket_event_type) + add_header_row(ws_content, "Connection Status:", request.connection_status) + add_header_row(ws_content, "Total Messages:", str(request.websocket_messages.size())) + # Request Headers section if not request.request_headers.is_empty(): var request_headers_section = create_collapsible_section("Request Headers", false) @@ -173,6 +196,32 @@ func update_headers_tab(request: NetworkRequest): add_header_row(response_headers_content, header_name + ":", str(request.response_headers[header_name])) func update_preview_tab(request: NetworkRequest): + if request.type == NetworkRequest.RequestType.SOCKET: + var content_to_show = "" + + match request.websocket_event_type: + "connection": + content_to_show = "WebSocket connection event\n" + content_to_show += "URL: " + request.url + "\n" + content_to_show += "Status: " + request.connection_status + "\n" + if request.status_code > 0: + content_to_show += "Status Code: " + str(request.status_code) + " " + request.status_text + "\n" + content_to_show += "Messages exchanged: " + str(request.websocket_messages.size()) + "close", "error": + content_to_show = "WebSocket " + request.websocket_event_type + " event\n" + content_to_show += "Status Code: " + str(request.status_code) + "\n" + content_to_show += "Reason: " + request.status_text + + if not content_to_show.is_empty(): + var code_edit = CodeEditUtils.create_code_edit({ + "text": content_to_show, + "editable": false, + "show_line_numbers": false, + "syntax_highlighter": null + }) + preview_tab.add_child(code_edit) + return + # For images, show the image in the preview tab if request.type == NetworkRequest.RequestType.IMG and request.status == NetworkRequest.RequestStatus.SUCCESS: var image = Image.new() @@ -240,6 +289,42 @@ func update_preview_tab(request: NetworkRequest): preview_tab.add_child(code_edit) func update_response_tab(request: NetworkRequest): + if request.type == NetworkRequest.RequestType.SOCKET: + var content_to_show = "" + + match request.websocket_event_type: + "connection": + content_to_show = "WebSocket Connection Details\n\n" + content_to_show += "This is a WebSocket connection request.\n" + content_to_show += "Connection Status: " + request.connection_status + "\n" + content_to_show += "WebSocket ID: " + request.websocket_id + "\n" + content_to_show += "Total Messages: " + str(request.websocket_messages.size()) + "\n" + if request.status_code > 0: + content_to_show += "Status Code: " + str(request.status_code) + " " + request.status_text + "\n" + content_to_show += "\nNote: Individual messages can be viewed in the 'Messages' tab." + "close", "error": + content_to_show = "WebSocket " + request.websocket_event_type.capitalize() + " Event\n\n" + content_to_show += "Status Code: " + str(request.status_code) + "\n" + content_to_show += "Reason: " + request.status_text + "\n" + content_to_show += "WebSocket ID: " + request.websocket_id + "\n" + content_to_show += "Total Messages Exchanged: " + str(request.websocket_messages.size()) + + if not content_to_show.is_empty(): + var code_edit = CodeEditUtils.create_code_edit({ + "text": content_to_show, + "editable": false, + "show_line_numbers": false, + "syntax_highlighter": null + }) + response_tab.add_child(code_edit) + else: + var label = Label.new() + label.text = "No WebSocket data to display" + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + response_tab.add_child(label) + return + if request.type == NetworkRequest.RequestType.IMG: var label = Label.new() label.text = "This response contains image data. See the \"Preview\" tab to view the image." @@ -280,6 +365,156 @@ func update_response_tab(request: NetworkRequest): response_tab.add_child(code_edit) +func update_messages_tab(request: NetworkRequest): + if request.type != NetworkRequest.RequestType.SOCKET: + return + + if request.websocket_messages.is_empty(): + var label = Label.new() + label.text = "No WebSocket messages yet" + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + messages_tab.add_child(label) + return + + var scroll_container = ScrollContainer.new() + scroll_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + scroll_container.size_flags_vertical = Control.SIZE_EXPAND_FILL + + var messages_container = VBoxContainer.new() + messages_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + scroll_container.add_child(messages_container) + + var header_container = VBoxContainer.new() + header_container.add_theme_constant_override("separation", 8) + + var search_container = HBoxContainer.new() + search_container.add_theme_constant_override("separation", 8) + + var search_label = Label.new() + search_label.text = "Search:" + search_label.add_theme_font_size_override("font_size", 11) + search_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7, 1.0)) + search_container.add_child(search_label) + + var search_input = LineEdit.new() + search_input.placeholder_text = "Filter messages..." + search_input.add_theme_font_size_override("font_size", 11) + search_input.custom_minimum_size.x = 200 + search_input.custom_minimum_size.y = 24 + search_container.add_child(search_input) + + var clear_button = Button.new() + clear_button.text = "Clear" + clear_button.add_theme_font_size_override("font_size", 10) + clear_button.custom_minimum_size.y = 24 + search_container.add_child(clear_button) + + header_container.add_child(search_container) + + var header = Label.new() + header.text = str(request.websocket_messages.size()) + " messages" + header.add_theme_font_size_override("font_size", 13) + header.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7, 1.0)) + header.custom_minimum_size.y = 20 + header.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + header_container.add_child(header) + + messages_container.add_child(header_container) + + var message_rows: Array[Control] = [] + var search_term = "" + + var update_search = func(): + var filter_text = search_input.text.to_lower() + var visible_count = 0 + + for row_index in range(message_rows.size()): + var row = message_rows[row_index] + var message = request.websocket_messages[row_index] + var should_show = filter_text.is_empty() or message.content.to_lower().contains(filter_text) + row.visible = should_show + if should_show: + visible_count += 1 + + if filter_text.is_empty(): + header.text = str(request.websocket_messages.size()) + " messages" + else: + header.text = str(visible_count) + " of " + str(request.websocket_messages.size()) + " messages" + + search_input.text_changed.connect(func(_text): update_search.call()) + clear_button.pressed.connect(func(): + search_input.text = "" + update_search.call() + ) + + for i in range(request.websocket_messages.size()): + var message = request.websocket_messages[i] + + var message_panel = PanelContainer.new() + message_panel.custom_minimum_size.y = 24 + + var panel_style = StyleBoxFlat.new() + if message.direction == "sent": + panel_style.bg_color = Color(0.2, 0.3, 0.5, 0.3) + else: + panel_style.bg_color = Color(0.2, 0.5, 0.3, 0.3) + + panel_style.content_margin_left = 6 + panel_style.content_margin_right = 6 + panel_style.content_margin_top = 2 + panel_style.content_margin_bottom = 2 + message_panel.add_theme_stylebox_override("panel", panel_style) + + var message_row = HBoxContainer.new() + message_row.add_theme_constant_override("separation", 8) + + var direction_label = Label.new() + var direction_icon = "↑" if message.direction == "sent" else "↓" + var direction_color = Color(0.7, 0.9, 1.0) if message.direction == "sent" else Color(0.7, 1.0, 0.9) + + direction_label.text = direction_icon + direction_label.add_theme_font_size_override("font_size", 12) + direction_label.add_theme_color_override("font_color", direction_color) + direction_label.custom_minimum_size.x = 16 + direction_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + message_row.add_child(direction_label) + + var timestamp_label = Label.new() + timestamp_label.text = message.get_formatted_time() + timestamp_label.add_theme_font_size_override("font_size", 10) + timestamp_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6, 1.0)) + timestamp_label.custom_minimum_size.x = 80 + message_row.add_child(timestamp_label) + + var content_label = Label.new() + var content_text = message.content + if content_text.length() > 60: + content_text = content_text.substr(0, 57) + "..." + content_text = content_text.replace("\n", " ").replace("\r", " ") + + content_label.text = content_text + content_label.add_theme_font_size_override("font_size", 11) + content_label.add_theme_color_override("font_color", Color(1.0, 1.0, 1.0, 1.0)) + content_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + content_label.clip_contents = true + content_label.autowrap_mode = TextServer.AUTOWRAP_OFF + message_row.add_child(content_label) + + var size_label = Label.new() + size_label.text = str(message.size) + "b" + size_label.add_theme_font_size_override("font_size", 10) + size_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6, 1.0)) + size_label.custom_minimum_size.x = 40 + size_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + message_row.add_child(size_label) + + message_panel.add_child(message_row) + messages_container.add_child(message_panel) + message_rows.append(message_panel) + + messages_tab.add_child(scroll_container) + func update_status_bar(): var total_requests = network_requests.size() var total_size = 0 diff --git a/flumi/Scripts/Utils/Lua/WebSocket.gd b/flumi/Scripts/Utils/Lua/WebSocket.gd index 71dfc83..0a1f7ff 100644 --- a/flumi/Scripts/Utils/Lua/WebSocket.gd +++ b/flumi/Scripts/Utils/Lua/WebSocket.gd @@ -23,6 +23,8 @@ class WebSocketWrapper: if connection_status: return + NetworkManager.call_deferred("start_websocket_connection", url, instance_id) + var error = websocket.connect_to_url(url) if error == OK: @@ -42,8 +44,10 @@ class WebSocketWrapper: timer.call_deferred("start") else: trigger_event("error", {"message": "No scene available for WebSocket timer"}) + NetworkManager.call_deferred("update_websocket_connection", instance_id, "error", 0, "No scene available") else: trigger_event("error", {"message": "Failed to connect to " + url + " (error: " + str(error) + ")"}) + NetworkManager.call_deferred("update_websocket_connection", instance_id, "error", 0, "Connection failed: " + str(error)) func _poll_websocket(): if not websocket: @@ -57,17 +61,25 @@ class WebSocketWrapper: if not connection_status: connection_status = true trigger_event("open", {}) + # Update NetworkManager with successful connection + NetworkManager.call_deferred("update_websocket_connection", instance_id, "open", 200, "Connected") # Check for messages while websocket.get_available_packet_count() > 0: var packet = websocket.get_packet() var message = packet.get_string_from_utf8() trigger_event("message", {"data": message}) + # Track received message in NetworkManager + NetworkManager.call_deferred("add_websocket_message", url, instance_id, "received", message) WebSocketPeer.STATE_CLOSED: if connection_status: connection_status = false trigger_event("close", {}) + # Update NetworkManager with closed connection + var close_code = websocket.get_close_code() + var close_reason = websocket.get_close_reason() + NetworkManager.call_deferred("update_websocket_connection", instance_id, "closed", close_code, close_reason) # Clean up timer if timer: @@ -82,25 +94,33 @@ class WebSocketWrapper: # Connection is closing if connection_status: connection_status = false + NetworkManager.call_deferred("update_websocket_connection", instance_id, "closing", 0, "Closing") _: # Unknown state or connection failed if connection_status: connection_status = false trigger_event("close", {}) + NetworkManager.call_deferred("update_websocket_connection", instance_id, "closed", 0, "Unexpected disconnection") elif not connection_status: # This might be a connection failure trigger_event("error", {"message": "Connection failed or was rejected by server"}) + NetworkManager.call_deferred("update_websocket_connection", instance_id, "error", 0, "Connection failed or rejected") func send_message(message: String): if connection_status and websocket: websocket.send_text(message) + # Track sent message in NetworkManager + NetworkManager.call_deferred("add_websocket_message", url, instance_id, "sent", message) func close_connection(): if websocket: websocket.close() connection_status = false + # Update NetworkManager with manual close + NetworkManager.call_deferred("update_websocket_connection", instance_id, "closed", 1000, "Manually closed") + if timer: timer.queue_free() timer = null From f24c1e34320148ecdbf38eeb7a96fcebe51f477b Mon Sep 17 00:00:00 2001 From: Aidan <76609332+redbaron2k7@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:39:40 -0500 Subject: [PATCH 2/3] not very clean --- flumi/Scripts/Browser/NetworkRequest.gd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flumi/Scripts/Browser/NetworkRequest.gd b/flumi/Scripts/Browser/NetworkRequest.gd index e09d3d1..913d426 100644 --- a/flumi/Scripts/Browser/NetworkRequest.gd +++ b/flumi/Scripts/Browser/NetworkRequest.gd @@ -210,9 +210,9 @@ static func format_bytes(given_size: int) -> String: if given_size < 1024: return str(given_size) + " B" elif given_size < 1024 * 1024: - return str(given_size / 1024.0) + " KB" + return str(given_size / 1024) + " KB" else: - return str(given_size / (1024.0 * 1024.0)) + " MB" + return str(given_size / (1024.0 * 1024)) + " MB" func get_time_display() -> String: if status == RequestStatus.PENDING: From ceeea3d1e7ef662e9d4c8c7fcefd26567ca6eeb3 Mon Sep 17 00:00:00 2001 From: Face <69168154+face-hh@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:47:23 +0300 Subject: [PATCH 3/3] improve UI and accessibility --- flumi/Scripts/Browser/NetworkRequest.gd | 24 +++--- flumi/Scripts/Browser/NetworkTab.gd | 101 +++++++++++++----------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/flumi/Scripts/Browser/NetworkRequest.gd b/flumi/Scripts/Browser/NetworkRequest.gd index 913d426..73f0125 100644 --- a/flumi/Scripts/Browser/NetworkRequest.gd +++ b/flumi/Scripts/Browser/NetworkRequest.gd @@ -48,20 +48,24 @@ var connection_status: String = "" # "connecting", "open", "closing", "closed" var websocket_messages: Array[WebSocketMessage] = [] class WebSocketMessage: - var timestamp: float + var hour: int + var minute: int + var second: int var direction: String # "sent" or "received" var content: String var size: int - + func _init(dir: String, msg: String): - timestamp = Time.get_ticks_msec() + var local_time = Time.get_datetime_dict_from_system(false) + hour = local_time.hour + minute = local_time.minute + second = local_time.second direction = dir content = msg size = msg.length() - + func get_formatted_time() -> String: - var time_obj = Time.get_datetime_dict_from_unix_time(timestamp / 1000.0) - return "%02d:%02d:%02d.%03d" % [time_obj.hour, time_obj.minute, time_obj.second, int(timestamp) % 1000] + return "%02d:%02d:%02d" % [hour, minute, second] func _init(request_url: String = "", request_method: String = "GET"): id = generate_id() @@ -88,11 +92,7 @@ func extract_name_from_url(request_url: String) -> String: if not websocket_event_type.is_empty(): match websocket_event_type: "connection": - var message_count = websocket_messages.size() - if message_count > 0: - return "WebSocket (" + str(message_count) + " messages)" - else: - return "WebSocket Connection" + return "WebSocket" "close": return "WebSocket Close" "error": @@ -254,8 +254,6 @@ func add_websocket_message(direction: String, message: String): var ws_message = WebSocketMessage.new(direction, message) websocket_messages.append(ws_message) - name = extract_name_from_url(url) - var total_message_size = 0 for msg in websocket_messages: total_message_size += msg.size diff --git a/flumi/Scripts/Browser/NetworkTab.gd b/flumi/Scripts/Browser/NetworkTab.gd index 983ea6e..00fb8e9 100644 --- a/flumi/Scripts/Browser/NetworkTab.gd +++ b/flumi/Scripts/Browser/NetworkTab.gd @@ -390,36 +390,44 @@ func update_messages_tab(request: NetworkRequest): var search_container = HBoxContainer.new() search_container.add_theme_constant_override("separation", 8) - - var search_label = Label.new() - search_label.text = "Search:" - search_label.add_theme_font_size_override("font_size", 11) - search_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7, 1.0)) - search_container.add_child(search_label) - + var search_input = LineEdit.new() - search_input.placeholder_text = "Filter messages..." - search_input.add_theme_font_size_override("font_size", 11) - search_input.custom_minimum_size.x = 200 - search_input.custom_minimum_size.y = 24 + search_input.placeholder_text = "Filter" + search_input.size_flags_horizontal = Control.SIZE_EXPAND_FILL + + var focus_style = StyleBoxFlat.new() + focus_style.content_margin_left = 16.0 + focus_style.content_margin_right = 8.0 + focus_style.bg_color = Color(0.168627, 0.168627, 0.168627, 1) + focus_style.border_width_left = 1 + focus_style.border_width_top = 1 + focus_style.border_width_right = 1 + focus_style.border_width_bottom = 1 + focus_style.border_color = Color(0.247059, 0.466667, 0.807843, 1) + focus_style.corner_radius_top_left = 15 + focus_style.corner_radius_top_right = 15 + focus_style.corner_radius_bottom_right = 15 + focus_style.corner_radius_bottom_left = 15 + search_input.add_theme_stylebox_override("focus", focus_style) + + var normal_style = StyleBoxFlat.new() + normal_style.content_margin_left = 16.0 + normal_style.content_margin_right = 8.0 + normal_style.bg_color = Color(0.168627, 0.168627, 0.168627, 1) + normal_style.corner_radius_top_left = 15 + normal_style.corner_radius_top_right = 15 + normal_style.corner_radius_bottom_right = 15 + normal_style.corner_radius_bottom_left = 15 + search_input.add_theme_stylebox_override("normal", normal_style) + search_container.add_child(search_input) - var clear_button = Button.new() - clear_button.text = "Clear" - clear_button.add_theme_font_size_override("font_size", 10) - clear_button.custom_minimum_size.y = 24 - search_container.add_child(clear_button) - header_container.add_child(search_container) - - var header = Label.new() - header.text = str(request.websocket_messages.size()) + " messages" - header.add_theme_font_size_override("font_size", 13) - header.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7, 1.0)) - header.custom_minimum_size.y = 20 - header.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - header_container.add_child(header) - + + var spacer = Control.new() + spacer.custom_minimum_size.y = 8 + header_container.add_child(spacer) + messages_container.add_child(header_container) var message_rows: Array[Control] = [] @@ -427,38 +435,35 @@ func update_messages_tab(request: NetworkRequest): var update_search = func(): var filter_text = search_input.text.to_lower() - var visible_count = 0 - + for row_index in range(message_rows.size()): var row = message_rows[row_index] var message = request.websocket_messages[row_index] var should_show = filter_text.is_empty() or message.content.to_lower().contains(filter_text) row.visible = should_show - if should_show: - visible_count += 1 - - if filter_text.is_empty(): - header.text = str(request.websocket_messages.size()) + " messages" - else: - header.text = str(visible_count) + " of " + str(request.websocket_messages.size()) + " messages" search_input.text_changed.connect(func(_text): update_search.call()) - clear_button.pressed.connect(func(): - search_input.text = "" - update_search.call() - ) for i in range(request.websocket_messages.size()): var message = request.websocket_messages[i] var message_panel = PanelContainer.new() - message_panel.custom_minimum_size.y = 24 + message_panel.custom_minimum_size.y = 32 + + var button = Button.new() + button.flat = true + button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + button.focus_mode = Control.FOCUS_NONE + + button.anchors_preset = Control.PRESET_FULL_RECT + button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + button.size_flags_vertical = Control.SIZE_EXPAND_FILL var panel_style = StyleBoxFlat.new() if message.direction == "sent": panel_style.bg_color = Color(0.2, 0.3, 0.5, 0.3) else: - panel_style.bg_color = Color(0.2, 0.5, 0.3, 0.3) + panel_style.bg_color = Color(0.0, 0.0, 0.0, 0.0) panel_style.content_margin_left = 6 panel_style.content_margin_right = 6 @@ -471,10 +476,10 @@ func update_messages_tab(request: NetworkRequest): var direction_label = Label.new() var direction_icon = "↑" if message.direction == "sent" else "↓" - var direction_color = Color(0.7, 0.9, 1.0) if message.direction == "sent" else Color(0.7, 1.0, 0.9) + var direction_color = Color(0.7, 0.9, 1.0) if message.direction == "sent" else Color(1.0, 0.7, 0.7) direction_label.text = direction_icon - direction_label.add_theme_font_size_override("font_size", 12) + direction_label.add_theme_font_size_override("font_size", 16) direction_label.add_theme_color_override("font_color", direction_color) direction_label.custom_minimum_size.x = 16 direction_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER @@ -482,7 +487,7 @@ func update_messages_tab(request: NetworkRequest): var timestamp_label = Label.new() timestamp_label.text = message.get_formatted_time() - timestamp_label.add_theme_font_size_override("font_size", 10) + timestamp_label.add_theme_font_size_override("font_size", 14) timestamp_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6, 1.0)) timestamp_label.custom_minimum_size.x = 80 message_row.add_child(timestamp_label) @@ -494,7 +499,7 @@ func update_messages_tab(request: NetworkRequest): content_text = content_text.replace("\n", " ").replace("\r", " ") content_label.text = content_text - content_label.add_theme_font_size_override("font_size", 11) + content_label.add_theme_font_size_override("font_size", 16) content_label.add_theme_color_override("font_color", Color(1.0, 1.0, 1.0, 1.0)) content_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL content_label.clip_contents = true @@ -503,13 +508,17 @@ func update_messages_tab(request: NetworkRequest): var size_label = Label.new() size_label.text = str(message.size) + "b" - size_label.add_theme_font_size_override("font_size", 10) + size_label.add_theme_font_size_override("font_size", 14) size_label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6, 1.0)) size_label.custom_minimum_size.x = 40 size_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT message_row.add_child(size_label) + button.pressed.connect(func(): DisplayServer.clipboard_set(message.content)) + message_panel.add_child(message_row) + message_panel.add_child(button) + messages_container.add_child(message_panel) message_rows.append(message_panel)