trace, console
This commit is contained in:
@@ -8,7 +8,7 @@ local password_input = gurt.select('#password')
|
||||
local log_output = gurt.select('#log-output')
|
||||
|
||||
function addLog(message)
|
||||
gurt.log(message)
|
||||
trace.log(message)
|
||||
log_output.text = log_output.text .. message .. '\\n'
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ local confirm_password_input = gurt.select('#confirm-password')
|
||||
local log_output = gurt.select('#log-output')
|
||||
|
||||
function addLog(message)
|
||||
gurt.log(message)
|
||||
trace.log(message)
|
||||
log_output.text = log_output.text .. message .. '\n'
|
||||
end
|
||||
|
||||
|
||||
1
flumi/Assets/Icons/eraser.svg
Normal file
1
flumi/Assets/Icons/eraser.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eraser-icon lucide-eraser"><path d="M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21"/><path d="m5.082 11.09 8.828 8.828"/></svg>
|
||||
|
After Width: | Height: | Size: 397 B |
37
flumi/Assets/Icons/eraser.svg.import
Normal file
37
flumi/Assets/Icons/eraser.svg.import
Normal file
@@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://custohlvwclqs"
|
||||
path="res://.godot/imported/eraser.svg-e6400a62a38066fbd6aaec113f0b7fb0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Icons/eraser.svg"
|
||||
dest_files=["res://.godot/imported/eraser.svg-e6400a62a38066fbd6aaec113f0b7fb0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
1
flumi/Assets/Icons/funnel.svg
Normal file
1
flumi/Assets/Icons/funnel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-funnel-icon lucide-funnel"><path d="M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"/></svg>
|
||||
|
After Width: | Height: | Size: 388 B |
37
flumi/Assets/Icons/funnel.svg.import
Normal file
37
flumi/Assets/Icons/funnel.svg.import
Normal file
@@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cqg4eny0nyojd"
|
||||
path="res://.godot/imported/funnel.svg-cd3708ed9907c314e118f9c06364d430.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Icons/funnel.svg"
|
||||
dest_files=["res://.godot/imported/funnel.svg-cd3708ed9907c314e118f9c06364d430.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
16
flumi/Resources/LuaSyntaxHighlighter.tres
Normal file
16
flumi/Resources/LuaSyntaxHighlighter.tres
Normal file
@@ -0,0 +1,16 @@
|
||||
[gd_resource type="SyntaxHighlighter" script_class="LuaSyntaxHighlighter" load_steps=2 format=3 uid="uid://d0aeuvwp0545i"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://qicpfnrmje1v" path="res://Scripts/LuaSyntaxHighlighter.gd" id="1_5xwa7"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_5xwa7")
|
||||
font_color = Color(1, 1, 1, 1)
|
||||
keyword_color = Color(1, 0.584314, 0.772549, 1)
|
||||
gurt_globals_color = Color(0.584314, 1, 0.635294, 1)
|
||||
function_color = Color(0.584314, 0.839216, 1, 1)
|
||||
member_color = Color(1, 1, 0.584314, 1)
|
||||
number_color = Color(0.772549, 0.584314, 1, 1)
|
||||
string_color = Color(1, 0.772549, 0.584314, 1)
|
||||
comment_color = Color(0.490196, 0.54902, 0.545098, 1)
|
||||
symbol_color = Color(0.470588, 0.87451, 0.827451, 1)
|
||||
metadata/_custom_type_script = "uid://qicpfnrmje1v"
|
||||
243
flumi/Scenes/DevTools.tscn
Normal file
243
flumi/Scenes/DevTools.tscn
Normal file
@@ -0,0 +1,243 @@
|
||||
[gd_scene load_steps=19 format=3 uid="uid://cgav3xl2xgupb"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://vrobqac6makc" path="res://Scripts/DevToolsConsole.gd" id="2_3m6n9"]
|
||||
[ext_resource type="Texture2D" uid="uid://custohlvwclqs" path="res://Assets/Icons/eraser.svg" id="3_6hj4c"]
|
||||
[ext_resource type="Texture2D" uid="uid://cqg4eny0nyojd" path="res://Assets/Icons/funnel.svg" id="4_ynqb1"]
|
||||
[ext_resource type="SyntaxHighlighter" uid="uid://d0aeuvwp0545i" path="res://Resources/LuaSyntaxHighlighter.tres" id="5_xkykt"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6hj4c"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6hj4c"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qb7hm"]
|
||||
content_margin_left = 15.0
|
||||
content_margin_top = 15.0
|
||||
content_margin_right = 15.0
|
||||
content_margin_bottom = 15.0
|
||||
bg_color = Color(0.137255, 0.137255, 0.137255, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ynqb1"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.247059, 0.466667, 0.807843, 1)
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8muo7"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.137255, 0.137255, 0.137255, 1)
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xkykt"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 8.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
|
||||
border_color = Color(0.8, 0.8, 1, 0)
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ynqb1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pqhy6"]
|
||||
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
|
||||
corner_radius_top_left = 50
|
||||
corner_radius_top_right = 50
|
||||
corner_radius_bottom_right = 50
|
||||
corner_radius_bottom_left = 50
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_apu5o"]
|
||||
bg_color = Color(0.6, 0.6, 0.6, 0)
|
||||
draw_center = false
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dderp"]
|
||||
bg_color = Color(0.6, 0.6, 0.6, 0)
|
||||
draw_center = false
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ko37l"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_right = 8.0
|
||||
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.247059, 0.466667, 0.807843, 1)
|
||||
corner_radius_top_left = 15
|
||||
corner_radius_top_right = 15
|
||||
corner_radius_bottom_right = 15
|
||||
corner_radius_bottom_left = 15
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_65n6c"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_right = 8.0
|
||||
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
|
||||
corner_radius_top_left = 15
|
||||
corner_radius_top_right = 15
|
||||
corner_radius_bottom_right = 15
|
||||
corner_radius_bottom_left = 15
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_up43w"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
expand_margin_left = 4.0
|
||||
expand_margin_right = 4.0
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qb3ke"]
|
||||
content_margin_left = 8.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.247059, 0.466667, 0.807843, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
expand_margin_left = 4.0
|
||||
expand_margin_right = 4.0
|
||||
|
||||
[node name="DevTools" type="VBoxContainer"]
|
||||
custom_minimum_size = Vector2(450, 400)
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="."]
|
||||
custom_minimum_size = Vector2(300, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_colors/drop_mark_color = Color(0.247059, 0.466667, 0.807843, 1)
|
||||
theme_override_styles/tab_focus = SubResource("StyleBoxEmpty_6hj4c")
|
||||
theme_override_styles/tabbar_background = SubResource("StyleBoxFlat_6hj4c")
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_qb7hm")
|
||||
theme_override_styles/tab_selected = SubResource("StyleBoxFlat_ynqb1")
|
||||
theme_override_styles/tab_hovered = SubResource("StyleBoxFlat_8muo7")
|
||||
theme_override_styles/tab_unselected = SubResource("StyleBoxFlat_xkykt")
|
||||
tab_alignment = 1
|
||||
current_tab = 0
|
||||
drag_to_rearrange_enabled = true
|
||||
|
||||
[node name="Console" type="VBoxContainer" parent="TabContainer"]
|
||||
layout_mode = 2
|
||||
script = ExtResource("2_3m6n9")
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="Toolbar" type="HBoxContainer" parent="TabContainer/Console"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="ClearButton" type="Button" parent="TabContainer/Console/Toolbar"]
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
theme_override_constants/icon_max_width = 20
|
||||
theme_override_styles/focus = SubResource("StyleBoxEmpty_ynqb1")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_pqhy6")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_apu5o")
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_dderp")
|
||||
icon = ExtResource("3_6hj4c")
|
||||
icon_alignment = 1
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="TabContainer/Console/Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/focus = SubResource("StyleBoxFlat_ko37l")
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_65n6c")
|
||||
placeholder_text = "Filter"
|
||||
right_icon = ExtResource("4_ynqb1")
|
||||
|
||||
[node name="Spacer2" type="Control" parent="TabContainer/Console"]
|
||||
custom_minimum_size = Vector2(0, 15)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Console"]
|
||||
custom_minimum_size = Vector2(0, 200)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LogContainer" type="VBoxContainer" parent="TabContainer/Console/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Spacer" type="Control" parent="TabContainer/Console"]
|
||||
custom_minimum_size = Vector2(0, 15)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="InputContainer" type="HBoxContainer" parent="TabContainer/Console"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="InputLine" type="CodeEdit" parent="TabContainer/Console/InputContainer"]
|
||||
clip_contents = false
|
||||
custom_minimum_size = Vector2(0, 35)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_up43w")
|
||||
theme_override_styles/focus = SubResource("StyleBoxFlat_qb3ke")
|
||||
text = "test"
|
||||
placeholder_text = "Enter Lua code..."
|
||||
scroll_fit_content_height = true
|
||||
caret_blink = true
|
||||
syntax_highlighter = ExtResource("5_xkykt")
|
||||
highlight_all_occurrences = true
|
||||
highlight_current_line = true
|
||||
symbol_tooltip_on_hover = true
|
||||
gutters_draw_line_numbers = true
|
||||
auto_brace_completion_enabled = true
|
||||
auto_brace_completion_highlight_matching = true
|
||||
|
||||
[node name="PositioningTimer" type="Timer" parent="TabContainer/Console/InputContainer"]
|
||||
|
||||
[node name="Elements" type="Label" parent="TabContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "Elements tab - Coming soon"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[node name="Sources" type="Label" parent="TabContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "Sources tab - Coming soon"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
metadata/_tab_index = 2
|
||||
|
||||
[node name="Network" type="Label" parent="TabContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "Network tab - Coming soon"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
metadata/_tab_index = 3
|
||||
|
||||
[node name="Application" type="Label" parent="TabContainer"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "Application tab - Coming soon"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
metadata/_tab_index = 4
|
||||
@@ -29,7 +29,7 @@ func _init():
|
||||
timeout_manager = LuaTimeoutManager.new()
|
||||
threaded_vm = ThreadedLuaVM.new()
|
||||
threaded_vm.script_completed.connect(_on_threaded_script_completed)
|
||||
threaded_vm.script_error.connect(func(e): print(e))
|
||||
threaded_vm.script_error.connect(_on_threaded_script_error)
|
||||
threaded_vm.dom_operation_request.connect(_handle_dom_operation)
|
||||
threaded_vm.print_output.connect(_on_print_output)
|
||||
|
||||
@@ -645,8 +645,11 @@ func execute_lua_script(code: String):
|
||||
func _on_threaded_script_completed(_result: Dictionary):
|
||||
pass
|
||||
|
||||
func _on_print_output(message: String):
|
||||
LuaPrintUtils.lua_print_direct(message)
|
||||
func _on_threaded_script_error(error_message: String):
|
||||
Trace.trace_error("RuntimeError: " + error_message)
|
||||
|
||||
func _on_print_output(message: Dictionary):
|
||||
Trace.get_instance().log_message.emit(message, "lua", Time.get_ticks_msec() / 1000.0)
|
||||
|
||||
func kill_script_execution():
|
||||
threaded_vm.stop_lua_thread()
|
||||
|
||||
574
flumi/Scripts/DevToolsConsole.gd
Normal file
574
flumi/Scripts/DevToolsConsole.gd
Normal file
@@ -0,0 +1,574 @@
|
||||
class_name DevToolsConsole
|
||||
extends VBoxContainer
|
||||
|
||||
@onready var log_container: VBoxContainer = $ScrollContainer/LogContainer
|
||||
@onready var input_line: CodeEdit = $InputContainer/InputLine
|
||||
@onready var scroll_container: ScrollContainer = $ScrollContainer
|
||||
@onready var clear_button: Button = $Toolbar/ClearButton
|
||||
@onready var filter_input: LineEdit = $Toolbar/LineEdit
|
||||
|
||||
var log_entries: Array[Dictionary] = []
|
||||
var current_filter: String = ""
|
||||
var last_log_item: Control = null
|
||||
var last_log_entry: Dictionary = {}
|
||||
var input_history: Array[String] = []
|
||||
var history_index: int = -1
|
||||
|
||||
func _ready():
|
||||
connect_signals()
|
||||
initialize_filter()
|
||||
load_existing_logs()
|
||||
|
||||
visibility_changed.connect(_on_visibility_changed)
|
||||
|
||||
func initialize_filter() -> void:
|
||||
filter_input.placeholder_text = "Filter"
|
||||
current_filter = ""
|
||||
|
||||
func connect_signals() -> void:
|
||||
Trace.get_instance().log_message.connect(_on_trace_message)
|
||||
clear_button.pressed.connect(_on_clear_pressed)
|
||||
|
||||
input_line.gui_input.connect(_on_input_gui_input)
|
||||
filter_input.text_changed.connect(_on_filter_changed)
|
||||
|
||||
func load_existing_logs() -> void:
|
||||
var existing_messages = Trace.get_all_messages()
|
||||
for msg in existing_messages:
|
||||
add_log_entry(msg.message, msg.level, msg.timestamp)
|
||||
|
||||
func _on_trace_message(message: Variant, level: String, timestamp: float) -> void:
|
||||
call_deferred("add_log_entry", message, level, timestamp)
|
||||
|
||||
func _on_lua_print(message: String) -> void:
|
||||
add_log_entry(message, "lua", Time.get_ticks_msec() / 1000.0)
|
||||
|
||||
func add_log_entry(message: Variant, level: String, timestamp: float) -> void:
|
||||
var entry = {
|
||||
"message": message,
|
||||
"level": level,
|
||||
"timestamp": timestamp,
|
||||
"count": 1
|
||||
}
|
||||
|
||||
if can_group_with_last_entry(entry):
|
||||
last_log_entry.count += 1
|
||||
last_log_entry.timestamp = timestamp
|
||||
log_entries[log_entries.size() - 1] = last_log_entry
|
||||
|
||||
update_log_item_display(last_log_item, last_log_entry)
|
||||
return
|
||||
|
||||
log_entries.append(entry)
|
||||
last_log_entry = entry
|
||||
|
||||
var should_add_separator = false
|
||||
if level == "log" or level == "lua" or level == "input":
|
||||
if log_container.get_child_count() > 0:
|
||||
var last_displayed_entry = get_last_displayed_entry()
|
||||
if last_displayed_entry and last_displayed_entry.level != "warning" and last_displayed_entry.level != "error":
|
||||
should_add_separator = true
|
||||
|
||||
if should_add_separator:
|
||||
var separator = HSeparator.new()
|
||||
separator.add_theme_color_override("separator", Color.GRAY * 0.3)
|
||||
log_container.add_child(separator)
|
||||
|
||||
var log_item = create_log_item(entry)
|
||||
log_container.add_child(log_item)
|
||||
last_log_item = log_item
|
||||
|
||||
apply_filter_to_item(log_item, entry)
|
||||
|
||||
call_deferred("_scroll_to_bottom")
|
||||
|
||||
func create_log_item(entry: Dictionary) -> Control:
|
||||
if entry.level == "input":
|
||||
var message_code_edit = CodeEdit.new()
|
||||
var input_display_text = get_display_text_for_entry(entry)
|
||||
message_code_edit.text = input_display_text
|
||||
message_code_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
message_code_edit.scroll_fit_content_height = true
|
||||
message_code_edit.editable = true
|
||||
message_code_edit.context_menu_enabled = true
|
||||
message_code_edit.shortcut_keys_enabled = true
|
||||
message_code_edit.selecting_enabled = true
|
||||
message_code_edit.deselect_on_focus_loss_enabled = true
|
||||
message_code_edit.drag_and_drop_selection_enabled = false
|
||||
message_code_edit.virtual_keyboard_enabled = false
|
||||
message_code_edit.middle_mouse_paste_enabled = false
|
||||
|
||||
var code_style_normal = StyleBoxFlat.new()
|
||||
code_style_normal.bg_color = Color.TRANSPARENT
|
||||
code_style_normal.border_width_left = 0
|
||||
code_style_normal.border_width_top = 0
|
||||
code_style_normal.border_width_right = 0
|
||||
code_style_normal.border_width_bottom = 0
|
||||
code_style_normal.content_margin_bottom = 8
|
||||
message_code_edit.add_theme_stylebox_override("normal", code_style_normal)
|
||||
message_code_edit.add_theme_stylebox_override("focus", code_style_normal)
|
||||
|
||||
message_code_edit.syntax_highlighter = input_line.syntax_highlighter.duplicate()
|
||||
|
||||
message_code_edit.gui_input.connect(_on_log_code_edit_gui_input)
|
||||
message_code_edit.focus_entered.connect(_on_log_code_edit_focus_entered.bind(message_code_edit))
|
||||
message_code_edit.text_changed.connect(_on_log_code_edit_text_changed.bind(message_code_edit, input_display_text))
|
||||
|
||||
return message_code_edit
|
||||
|
||||
if entry.level == "lua" and entry.message is Dictionary and entry.message.has("parts"):
|
||||
return create_structured_log_item(entry)
|
||||
|
||||
var panel = PanelContainer.new()
|
||||
var style_box = StyleBoxFlat.new()
|
||||
|
||||
match entry.level:
|
||||
"warning":
|
||||
style_box.bg_color = Color.YELLOW
|
||||
style_box.bg_color.a = 0.2
|
||||
style_box.corner_radius_top_left = 6
|
||||
style_box.corner_radius_top_right = 6
|
||||
style_box.corner_radius_bottom_left = 6
|
||||
style_box.corner_radius_bottom_right = 6
|
||||
style_box.content_margin_left = 8
|
||||
style_box.content_margin_right = 8
|
||||
style_box.content_margin_top = 4
|
||||
style_box.content_margin_bottom = 4
|
||||
"error":
|
||||
style_box.bg_color = Color.RED
|
||||
style_box.bg_color.a = 0.2
|
||||
style_box.corner_radius_top_left = 6
|
||||
style_box.corner_radius_top_right = 6
|
||||
style_box.corner_radius_bottom_left = 6
|
||||
style_box.corner_radius_bottom_right = 6
|
||||
style_box.content_margin_left = 8
|
||||
style_box.content_margin_right = 8
|
||||
style_box.content_margin_top = 4
|
||||
style_box.content_margin_bottom = 4
|
||||
_:
|
||||
style_box.bg_color = Color.TRANSPARENT
|
||||
|
||||
panel.add_theme_stylebox_override("panel", style_box)
|
||||
|
||||
var container: Control
|
||||
if entry.level == "warning" or entry.level == "error":
|
||||
var margin_container = MarginContainer.new()
|
||||
margin_container.add_child(panel)
|
||||
container = margin_container
|
||||
else:
|
||||
container = panel
|
||||
|
||||
var message_label = Label.new()
|
||||
var display_text = get_display_text_for_entry(entry)
|
||||
message_label.text = display_text
|
||||
message_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
message_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
|
||||
match entry.level:
|
||||
"warning":
|
||||
message_label.add_theme_color_override("font_color", Color.YELLOW)
|
||||
"error":
|
||||
message_label.add_theme_color_override("font_color", Color.WHITE)
|
||||
_:
|
||||
message_label.add_theme_color_override("font_color", Color.WHITE)
|
||||
|
||||
panel.add_child(message_label)
|
||||
return container
|
||||
|
||||
func _scroll_to_bottom() -> void:
|
||||
if scroll_container:
|
||||
scroll_container.scroll_vertical = int(scroll_container.get_v_scroll_bar().max_value)
|
||||
|
||||
func _on_clear_pressed() -> void:
|
||||
for child in log_container.get_children():
|
||||
child.queue_free()
|
||||
log_entries.clear()
|
||||
|
||||
last_log_item = null
|
||||
last_log_entry = {}
|
||||
|
||||
Trace.clear_messages()
|
||||
|
||||
func _on_input_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed:
|
||||
if event.keycode == KEY_ENTER and event.ctrl_pressed:
|
||||
var text = input_line.text.strip_edges()
|
||||
if not text.is_empty():
|
||||
_on_input_submitted(text)
|
||||
input_line.text = ""
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event.keycode == KEY_UP and not event.ctrl_pressed and not event.shift_pressed and not event.alt_pressed:
|
||||
if input_line.get_caret_column() == 0 and input_line.get_caret_line() == 0:
|
||||
navigate_history_up()
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event.keycode == KEY_DOWN and not event.ctrl_pressed and not event.shift_pressed and not event.alt_pressed:
|
||||
var caret_pos = input_line.get_caret_column()
|
||||
var last_line = input_line.get_line_count() - 1
|
||||
var last_line_length = input_line.get_line(last_line).length()
|
||||
if input_line.get_caret_line() == last_line and caret_pos == last_line_length:
|
||||
navigate_history_down()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
func navigate_history_up() -> void:
|
||||
if input_history.is_empty():
|
||||
return
|
||||
if history_index == -1:
|
||||
history_index = input_history.size() - 1
|
||||
elif history_index > 0:
|
||||
history_index -= 1
|
||||
|
||||
if history_index >= 0 and history_index < input_history.size():
|
||||
input_line.text = input_history[history_index]
|
||||
call_deferred("move_caret_to_end")
|
||||
|
||||
func navigate_history_down() -> void:
|
||||
if input_history.is_empty() or history_index == -1:
|
||||
return
|
||||
|
||||
history_index += 1
|
||||
|
||||
if history_index >= input_history.size():
|
||||
history_index = -1
|
||||
input_line.text = ""
|
||||
else:
|
||||
input_line.text = input_history[history_index]
|
||||
call_deferred("move_caret_to_end")
|
||||
|
||||
func move_caret_to_end() -> void:
|
||||
var last_line = input_line.get_line_count() - 1
|
||||
var last_line_length = input_line.get_line(last_line).length()
|
||||
input_line.set_caret_line(last_line)
|
||||
input_line.set_caret_column(last_line_length)
|
||||
|
||||
func _on_input_submitted(text: String) -> void:
|
||||
if text.strip_edges().is_empty():
|
||||
return
|
||||
|
||||
if input_history.is_empty() or input_history[input_history.size() - 1] != text:
|
||||
input_history.append(text)
|
||||
if input_history.size() > 100:
|
||||
input_history.pop_front()
|
||||
|
||||
history_index = -1
|
||||
|
||||
add_log_entry("> " + text, "input", Time.get_ticks_msec() / 1000.0)
|
||||
|
||||
execute_lua_command(text)
|
||||
|
||||
func execute_lua_command(code: String) -> void:
|
||||
var main_scene = Engine.get_main_loop().current_scene
|
||||
if main_scene and main_scene.has_method("get_active_tab"):
|
||||
var active_tab = main_scene.get_active_tab()
|
||||
if active_tab and active_tab.lua_apis.size() > 0:
|
||||
var lua_api = active_tab.lua_apis[0]
|
||||
if lua_api:
|
||||
var is_expression = is_likely_expression(code)
|
||||
if is_expression:
|
||||
var wrapped_code = "print(" + code + ")"
|
||||
lua_api.execute_lua_script(wrapped_code)
|
||||
else:
|
||||
lua_api.execute_lua_script(code)
|
||||
return
|
||||
|
||||
add_log_entry("No Lua context available", "error", Time.get_ticks_msec() / 1000.0)
|
||||
|
||||
func is_likely_expression(code: String) -> bool:
|
||||
var trimmed = code.strip_edges()
|
||||
var statement_keywords = ["if", "for", "while", "do", "function", "local", "return", "break"]
|
||||
for keyword in statement_keywords:
|
||||
if trimmed.begins_with(keyword + " ") or trimmed.begins_with(keyword + "("):
|
||||
return false
|
||||
if "=" in trimmed and not ("==" in trimmed or "!=" in trimmed or ">=" in trimmed or "<=" in trimmed):
|
||||
return false
|
||||
if "print(" in trimmed or "console.log(" in trimmed or "_trace_" in trimmed:
|
||||
return false
|
||||
if trimmed.ends_with(")") or trimmed.ends_with("]") or not (" " in trimmed):
|
||||
return true
|
||||
return true
|
||||
|
||||
func _on_filter_changed(new_text: String) -> void:
|
||||
current_filter = new_text.strip_edges()
|
||||
refresh_log_display()
|
||||
|
||||
func refresh_log_display() -> void:
|
||||
for i in range(log_container.get_child_count()):
|
||||
var child = log_container.get_child(i)
|
||||
if child is HSeparator:
|
||||
child.visible = should_separator_be_visible(i)
|
||||
else:
|
||||
var entry_index = get_entry_index_for_child(i)
|
||||
if entry_index >= 0 and entry_index < log_entries.size():
|
||||
var entry = log_entries[entry_index]
|
||||
apply_filter_to_item(child, entry)
|
||||
|
||||
call_deferred("_scroll_to_bottom")
|
||||
|
||||
func apply_filter_to_item(item: Control, entry: Dictionary) -> void:
|
||||
item.visible = entry_matches_filter(entry)
|
||||
|
||||
func entry_matches_filter(entry: Dictionary) -> bool:
|
||||
if current_filter.is_empty():
|
||||
return true
|
||||
|
||||
var message_text = ""
|
||||
if entry.message is Dictionary and entry.message.has("parts"):
|
||||
message_text = get_display_text_for_entry(entry)
|
||||
else:
|
||||
message_text = str(entry.message)
|
||||
|
||||
if current_filter.to_lower() in message_text.to_lower():
|
||||
return true
|
||||
|
||||
if current_filter.to_lower() == entry.level.to_lower():
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func get_last_displayed_entry() -> Dictionary:
|
||||
for i in range(log_entries.size() - 2, -1, -1): # Start from second-to-last entry
|
||||
var entry = log_entries[i]
|
||||
if entry_matches_filter(entry):
|
||||
return entry
|
||||
return {}
|
||||
|
||||
func should_separator_be_visible(separator_index: int) -> bool:
|
||||
var before_visible = false
|
||||
var after_visible = false
|
||||
|
||||
if separator_index > 0:
|
||||
var before_child = log_container.get_child(separator_index - 1)
|
||||
before_visible = before_child.visible
|
||||
|
||||
|
||||
if separator_index < log_container.get_child_count() - 1:
|
||||
var after_child = log_container.get_child(separator_index + 1)
|
||||
after_visible = after_child.visible
|
||||
|
||||
return before_visible and after_visible
|
||||
|
||||
func get_entry_index_for_child(child_index: int) -> int:
|
||||
var entry_index = 0
|
||||
for i in range(child_index):
|
||||
var child = log_container.get_child(i)
|
||||
if not child is HSeparator:
|
||||
entry_index += 1
|
||||
return entry_index
|
||||
|
||||
func can_group_with_last_entry(entry: Dictionary) -> bool:
|
||||
if last_log_entry.is_empty() or last_log_item == null:
|
||||
return false
|
||||
|
||||
if entry.level != last_log_entry.level:
|
||||
return false
|
||||
|
||||
var current_message_text = ""
|
||||
var last_message_text = ""
|
||||
|
||||
if entry.message is Dictionary and entry.message.has("parts"):
|
||||
current_message_text = get_display_text_for_entry(entry)
|
||||
else:
|
||||
current_message_text = str(entry.message)
|
||||
|
||||
if last_log_entry.message is Dictionary and last_log_entry.message.has("parts"):
|
||||
last_message_text = get_display_text_for_entry(last_log_entry)
|
||||
else:
|
||||
last_message_text = str(last_log_entry.message)
|
||||
|
||||
if current_message_text != last_message_text:
|
||||
return false
|
||||
|
||||
if entry.level == "input":
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
func update_log_item_display(log_item: Control, entry: Dictionary) -> void:
|
||||
if entry.level == "input":
|
||||
var code_edit = log_item as CodeEdit
|
||||
if code_edit:
|
||||
code_edit.text = get_display_text_for_entry(entry)
|
||||
else:
|
||||
var label = find_message_label_in_item(log_item)
|
||||
if label:
|
||||
label.text = get_display_text_for_entry(entry)
|
||||
|
||||
func find_message_code_edit_in_item(item: Control) -> CodeEdit:
|
||||
if item is CodeEdit:
|
||||
return item as CodeEdit
|
||||
|
||||
if item is MarginContainer:
|
||||
var panel = item.get_child(0) as PanelContainer
|
||||
if panel:
|
||||
return panel.get_child(0) as CodeEdit
|
||||
elif item is PanelContainer:
|
||||
return item.get_child(0) as CodeEdit
|
||||
return null
|
||||
|
||||
func find_message_label_in_item(item: Control) -> Label:
|
||||
if item is MarginContainer:
|
||||
var panel = item.get_child(0) as PanelContainer
|
||||
if panel:
|
||||
return panel.get_child(0) as Label
|
||||
elif item is PanelContainer:
|
||||
return item.get_child(0) as Label
|
||||
return null
|
||||
|
||||
func _on_log_code_edit_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed:
|
||||
var key = event.keycode
|
||||
if event.ctrl_pressed and (key == KEY_C or key == KEY_A):
|
||||
return
|
||||
if key in [KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN]:
|
||||
return
|
||||
if event.shift_pressed and key in [KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME, KEY_END]:
|
||||
return
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event is InputEventMouseButton:
|
||||
return
|
||||
|
||||
func _on_log_code_edit_focus_entered(code_edit: CodeEdit) -> void:
|
||||
code_edit.release_focus()
|
||||
|
||||
func _on_log_code_edit_text_changed(code_edit: CodeEdit, original_text: String) -> void:
|
||||
if code_edit.text != original_text:
|
||||
code_edit.text = original_text
|
||||
|
||||
func get_display_text_for_entry(entry: Dictionary) -> String:
|
||||
var count = entry.get("count", 1)
|
||||
var message = entry.message
|
||||
|
||||
var base_text = ""
|
||||
if message is Dictionary and message.has("parts"):
|
||||
var parts = message.parts
|
||||
var text_parts = []
|
||||
for part in parts:
|
||||
if part.type == "primitive":
|
||||
text_parts.append(str(part.data))
|
||||
elif part.type == "table":
|
||||
var key_count = part.data.keys().size()
|
||||
text_parts.append("Object {" + str(key_count) + "}")
|
||||
base_text = "\t".join(text_parts)
|
||||
else:
|
||||
base_text = str(message)
|
||||
|
||||
if count > 1:
|
||||
return "(" + str(count) + ") " + base_text
|
||||
else:
|
||||
return base_text
|
||||
|
||||
func create_structured_log_item(entry) -> Control:
|
||||
var container = VBoxContainer.new()
|
||||
var parts = entry.message.parts
|
||||
if parts.size() == 1 and parts[0].type == "primitive":
|
||||
var simple_panel = PanelContainer.new()
|
||||
var style_box = StyleBoxFlat.new()
|
||||
style_box.bg_color = Color.TRANSPARENT
|
||||
style_box.content_margin_left = 10
|
||||
style_box.content_margin_top = 5
|
||||
style_box.content_margin_right = 10
|
||||
style_box.content_margin_bottom = 5
|
||||
simple_panel.add_theme_stylebox_override("panel", style_box)
|
||||
var label = Label.new()
|
||||
label.text = str(parts[0].data)
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
simple_panel.add_child(label)
|
||||
container.add_child(simple_panel)
|
||||
return container
|
||||
|
||||
for i in range(parts.size()):
|
||||
var part = parts[i]
|
||||
|
||||
if part.type == "primitive":
|
||||
var text_panel = PanelContainer.new()
|
||||
var style_box = StyleBoxFlat.new()
|
||||
style_box.bg_color = Color.TRANSPARENT
|
||||
style_box.content_margin_left = 10
|
||||
style_box.content_margin_top = 2
|
||||
style_box.content_margin_right = 10
|
||||
style_box.content_margin_bottom = 2
|
||||
text_panel.add_theme_stylebox_override("panel", style_box)
|
||||
|
||||
var label = Label.new()
|
||||
label.text = str(part.data)
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
text_panel.add_child(label)
|
||||
container.add_child(text_panel)
|
||||
|
||||
elif part.type == "table":
|
||||
var table_item = create_expandable_table_item(part.data)
|
||||
container.add_child(table_item)
|
||||
|
||||
return container
|
||||
|
||||
func create_expandable_table_item(table_data) -> Control:
|
||||
var main_container = VBoxContainer.new()
|
||||
|
||||
var header_container = HBoxContainer.new()
|
||||
header_container.custom_minimum_size.y = 24
|
||||
|
||||
var chevron_button = Button.new()
|
||||
chevron_button.text = "▶"
|
||||
chevron_button.custom_minimum_size = Vector2(20, 20)
|
||||
chevron_button.flat = true
|
||||
chevron_button.focus_mode = Control.FOCUS_NONE
|
||||
header_container.add_child(chevron_button)
|
||||
|
||||
var summary_label = Label.new()
|
||||
var key_count = table_data.keys().size()
|
||||
if table_data.has("__type"):
|
||||
summary_label.text = str(table_data.get("__type", "Object")) + " {" + str(key_count) + "}"
|
||||
else:
|
||||
summary_label.text = "Object {" + str(key_count) + "}"
|
||||
summary_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
summary_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_container.add_child(summary_label)
|
||||
|
||||
main_container.add_child(header_container)
|
||||
|
||||
var content_container = VBoxContainer.new()
|
||||
content_container.visible = false
|
||||
|
||||
for key in table_data.keys():
|
||||
if key == "__type":
|
||||
continue
|
||||
|
||||
var value = table_data[key]
|
||||
var row_container = HBoxContainer.new()
|
||||
|
||||
var key_label = Label.new()
|
||||
key_label.text = str(key) + ": "
|
||||
key_label.custom_minimum_size.x = 80
|
||||
key_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP
|
||||
row_container.add_child(key_label)
|
||||
|
||||
var value_label = Label.new()
|
||||
if value is Dictionary:
|
||||
var nested_count = value.keys().size()
|
||||
value_label.text = "Object {" + str(nested_count) + "}"
|
||||
value_label.modulate = Color.GRAY
|
||||
elif value is Array:
|
||||
value_label.text = "Array [" + str(value.size()) + "]"
|
||||
value_label.modulate = Color.GRAY
|
||||
else:
|
||||
value_label.text = str(value)
|
||||
|
||||
value_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
value_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP
|
||||
value_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row_container.add_child(value_label)
|
||||
|
||||
content_container.add_child(row_container)
|
||||
|
||||
main_container.add_child(content_container)
|
||||
|
||||
chevron_button.pressed.connect(func():
|
||||
content_container.visible = !content_container.visible
|
||||
chevron_button.text = "▼" if content_container.visible else "▶"
|
||||
)
|
||||
|
||||
return main_container
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if visible:
|
||||
input_line.grab_focus()
|
||||
1
flumi/Scripts/DevToolsConsole.gd.uid
Normal file
1
flumi/Scripts/DevToolsConsole.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vrobqac6makc
|
||||
1
flumi/Scripts/LuaCodeEdit.gd.uid
Normal file
1
flumi/Scripts/LuaCodeEdit.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cer1rniskhi24
|
||||
190
flumi/Scripts/LuaSyntaxHighlighter.gd
Normal file
190
flumi/Scripts/LuaSyntaxHighlighter.gd
Normal file
@@ -0,0 +1,190 @@
|
||||
@tool
|
||||
class_name LuaSyntaxHighlighter
|
||||
extends SyntaxHighlighter
|
||||
|
||||
@export_group("Colors")
|
||||
@export var font_color: Color = Color("#d4d4d4", Color.WHITE)
|
||||
@export var keyword_color: Color = Color.from_string("#c586c0", Color.WHITE)
|
||||
@export var gurt_globals_color: Color = Color.from_string("#569cd6", Color.WHITE)
|
||||
@export var function_color: Color = Color.from_string("#dcdcaa", Color.WHITE)
|
||||
@export var member_color: Color = Color.from_string("#9cdcfe", Color.WHITE)
|
||||
@export var number_color: Color = Color.from_string("#b5cea8", Color.WHITE)
|
||||
@export var string_color: Color = Color.from_string("#ce9178", Color.WHITE)
|
||||
@export var comment_color: Color = Color.from_string("#6a9955", Color.WHITE)
|
||||
@export var symbol_color: Color = Color.from_string("#c586c0", Color.WHITE)
|
||||
|
||||
enum State { DEFAULT, IN_MULTILINE_COMMENT, IN_MULTILINE_STRING }
|
||||
var _state_cache: Dictionary = {}
|
||||
|
||||
var _keywords: Dictionary = {}
|
||||
var _built_in_functions: Dictionary = {}
|
||||
var _gurt_globals: Dictionary = {}
|
||||
var _member_keywords: Dictionary = {}
|
||||
|
||||
func _init() -> void:
|
||||
for k in ["and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"]:
|
||||
_keywords[k] = true
|
||||
|
||||
for f in ["assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "load", "loadfile", "next", "pairs", "pcall", "print", "rawequal", "rawget", "rawlen", "rawset", "require", "select", "setmetatable", "tonumber", "tostring", "type", "xpcall"]:
|
||||
_built_in_functions[f] = true
|
||||
|
||||
for g in ["gurt", "trace", "JSON", "Time", "WebSocket", "Clipboard", "Regex", "setTimeout", "setInterval", "clearTimeout", "clearInterval", "fetch", "urlEncode", "urlDecode"]:
|
||||
_gurt_globals[g] = true
|
||||
|
||||
var members = [
|
||||
"select", "selectAll", "create", "body", "location", "href", "reload", "goto", "query", "get", "has", "getAll",
|
||||
"log", "warn", "error", "text", "value", "visible", "children", "parent", "nextSibling", "previousSibling",
|
||||
"firstChild", "lastChild", "classList", "on", "append", "remove", "insertBefore", "insertAfter", "replace",
|
||||
"clone", "getAttribute", "setAttribute", "show", "hide", "focus", "unfocus", "createTween", "unsubscribe",
|
||||
"add", "remove", "toggle", "contains", "item", "length", "to", "duration", "easing", "transition", "play",
|
||||
"pause", "stop", "currentTime", "volume", "loop", "src", "playing", "paused", "withContext", "fillRect",
|
||||
"strokeRect", "clearRect", "drawCircle", "drawText", "setFont", "measureText", "beginPath", "moveTo",
|
||||
"lineTo", "closePath", "stroke", "fill", "arc", "quadraticCurveTo", "bezierCurveTo", "setStrokeStyle",
|
||||
"setFillStyle", "setLineWidth", "save", "translate", "rotate", "scale", "restore", "source", "ok", "json",
|
||||
"status", "statusText", "headers", "stringify", "parse", "now", "format", "date", "sleep", "benchmark",
|
||||
"timer", "delay", "elapsed", "reset", "complete", "remaining", "new", "send", "close", "readyState",
|
||||
"test", "match", "tostring", "replace", "replaceAll", "trim",
|
||||
]
|
||||
for m in members:
|
||||
_member_keywords[m] = true
|
||||
|
||||
func _is_whitespace(char: String) -> bool:
|
||||
return char == " " or char == "\t"
|
||||
|
||||
|
||||
func _clear_highlighting_cache():
|
||||
_state_cache.clear()
|
||||
|
||||
func _get_initial_state() -> int:
|
||||
return State.DEFAULT
|
||||
|
||||
func _get_line_state(p_line: int, p_state: int) -> int:
|
||||
var current_state: int = p_state
|
||||
var line_text: String = get_text_edit().get_line(p_line)
|
||||
var line_len: int = line_text.length()
|
||||
var i := 0
|
||||
while i < line_len:
|
||||
if current_state == State.DEFAULT:
|
||||
if i + 3 < line_len and line_text.substr(i, 4) == "--[[":
|
||||
current_state = State.IN_MULTILINE_COMMENT
|
||||
i += 4
|
||||
continue
|
||||
if i + 1 < line_len and line_text.substr(i, 2) == "[[":
|
||||
current_state = State.IN_MULTILINE_STRING
|
||||
i += 2
|
||||
continue
|
||||
if line_text[i] == "'" or line_text[i] == "\"":
|
||||
var quote = line_text[i]
|
||||
i += 1
|
||||
while i < line_len:
|
||||
if line_text[i] == "\\": i += 2; continue
|
||||
if line_text[i] == quote: break
|
||||
i += 1
|
||||
else:
|
||||
var end_idx = line_text.find("]]", i)
|
||||
if end_idx != -1:
|
||||
current_state = State.DEFAULT
|
||||
i = end_idx + 2
|
||||
continue
|
||||
else:
|
||||
i = line_len
|
||||
i += 1
|
||||
_state_cache[p_line] = current_state
|
||||
return current_state
|
||||
|
||||
func _get_line_syntax_highlighting(p_line: int) -> Dictionary:
|
||||
var color_map := {}
|
||||
var start_state: int = _state_cache.get(p_line - 1, _get_initial_state())
|
||||
|
||||
var line_text: String = get_text_edit().get_line(p_line)
|
||||
var line_len: int = line_text.length()
|
||||
|
||||
color_map[0] = {"color": font_color}
|
||||
|
||||
var i := 0
|
||||
if start_state != State.DEFAULT:
|
||||
var end_idx = line_text.find("]]")
|
||||
var region_color = comment_color if start_state == State.IN_MULTILINE_COMMENT else string_color
|
||||
|
||||
color_map[0] = {"color": region_color}
|
||||
if end_idx == -1:
|
||||
return color_map
|
||||
else:
|
||||
i = end_idx + 2
|
||||
if i < line_len:
|
||||
color_map[i] = {"color": font_color}
|
||||
start_state = State.DEFAULT
|
||||
while i < line_len:
|
||||
var start_col: int = i
|
||||
var current_char: String = line_text[i]
|
||||
|
||||
if current_char == "-" and i + 1 < line_len and line_text[i+1] == "-":
|
||||
if not (i + 3 < line_len and line_text.substr(i, 4) == "--[["):
|
||||
color_map[i] = {"color": comment_color}
|
||||
return color_map
|
||||
|
||||
if current_char == "\"" or current_char == "'":
|
||||
var quote = current_char
|
||||
var string_start = i
|
||||
i += 1
|
||||
while i < line_len:
|
||||
if line_text[i] == "\\": i += 2; continue
|
||||
if line_text[i] == quote: i += 1; break
|
||||
i += 1
|
||||
var string_end = i
|
||||
color_map[string_start] = {"color": string_color}
|
||||
if string_end < line_len:
|
||||
color_map[string_end] = {"color": font_color}
|
||||
continue
|
||||
|
||||
if current_char.is_valid_int() or (current_char == "." and i + 1 < line_len and line_text[i+1].is_valid_int()):
|
||||
var is_hex = false
|
||||
if current_char == "0" and i + 1 < line_len and line_text[i+1].to_lower() == "x":
|
||||
i += 2; is_hex = true
|
||||
while i < line_len:
|
||||
var char = line_text[i]
|
||||
if (is_hex and char.is_valid_hex_number(false)) or \
|
||||
(not is_hex and (char.is_valid_int() or char in "Ee.-+")):
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
var number_end = i
|
||||
color_map[start_col] = {"color": number_color}
|
||||
if number_end < line_len:
|
||||
color_map[number_end] = {"color": font_color}
|
||||
continue
|
||||
|
||||
if current_char.is_valid_identifier() and not current_char.is_valid_int():
|
||||
while i < line_len and line_text[i].is_valid_identifier():
|
||||
i += 1
|
||||
var word = line_text.substr(start_col, i - start_col)
|
||||
|
||||
var color = font_color
|
||||
if _keywords.has(word): color = keyword_color
|
||||
elif _gurt_globals.has(word): color = gurt_globals_color
|
||||
elif _built_in_functions.has(word): color = function_color
|
||||
else:
|
||||
var prev_char_idx = start_col - 1
|
||||
while prev_char_idx >= 0 and _is_whitespace(line_text[prev_char_idx]):
|
||||
prev_char_idx -= 1
|
||||
var next_char_idx = i
|
||||
while next_char_idx < line_len and _is_whitespace(line_text[next_char_idx]):
|
||||
next_char_idx += 1
|
||||
|
||||
var is_member = prev_char_idx >= 0 and line_text[prev_char_idx] in [".", ":"]
|
||||
var is_function_call = next_char_idx < line_len and line_text[next_char_idx] == "("
|
||||
|
||||
if is_member and _member_keywords.has(word): color = member_color
|
||||
elif is_function_call: color = function_color
|
||||
|
||||
if color != font_color: color_map[start_col] = {"color": color}
|
||||
continue
|
||||
|
||||
if not _is_whitespace(current_char):
|
||||
color_map[i] = {"color": symbol_color}
|
||||
if i + 1 < line_len:
|
||||
color_map[i + 1] = {"color": font_color}
|
||||
|
||||
i += 1
|
||||
|
||||
return color_map
|
||||
1
flumi/Scripts/LuaSyntaxHighlighter.gd.uid
Normal file
1
flumi/Scripts/LuaSyntaxHighlighter.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://qicpfnrmje1v
|
||||
@@ -35,6 +35,9 @@ var loading_tween: Tween
|
||||
var scroll_container: ScrollContainer = null
|
||||
var website_container: VBoxContainer = null
|
||||
var background_panel: PanelContainer = null
|
||||
var main_hbox: HBoxContainer = null
|
||||
var dev_tools: Control = null
|
||||
var dev_tools_visible: bool = false
|
||||
var lua_apis: Array[LuaAPI] = []
|
||||
var current_url: String = ""
|
||||
var has_content: bool = false
|
||||
@@ -131,6 +134,9 @@ func _exit_tree():
|
||||
background_panel.get_parent().remove_child(background_panel)
|
||||
background_panel.queue_free()
|
||||
|
||||
if dev_tools and is_instance_valid(dev_tools):
|
||||
dev_tools.queue_free()
|
||||
|
||||
remove_from_group("tabs")
|
||||
|
||||
func init_scene(parent_container: Control) -> void:
|
||||
@@ -144,9 +150,15 @@ func init_scene(parent_container: Control) -> void:
|
||||
style_box.bg_color = Color(1, 1, 1, 1) # White background
|
||||
background_panel.add_theme_stylebox_override("panel", style_box)
|
||||
|
||||
main_hbox = HBoxContainer.new()
|
||||
main_hbox.name = "Tab_MainHBox_" + str(get_instance_id())
|
||||
main_hbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
main_hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
|
||||
scroll_container = ScrollContainer.new()
|
||||
scroll_container.name = "Tab_ScrollContainer_" + str(get_instance_id())
|
||||
scroll_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL # Take 2/3 of space
|
||||
|
||||
website_container = VBoxContainer.new()
|
||||
website_container.name = "Tab_WebsiteContainer_" + str(get_instance_id())
|
||||
@@ -154,8 +166,15 @@ func init_scene(parent_container: Control) -> void:
|
||||
website_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
website_container.add_theme_constant_override("separation", 22)
|
||||
|
||||
var dev_tools_scene = preload("res://Scenes/DevTools.tscn")
|
||||
dev_tools = dev_tools_scene.instantiate()
|
||||
dev_tools.name = "Tab_DevTools_" + str(get_instance_id())
|
||||
dev_tools.visible = false
|
||||
|
||||
parent_container.call_deferred("add_child", background_panel)
|
||||
background_panel.call_deferred("add_child", scroll_container)
|
||||
background_panel.call_deferred("add_child", main_hbox)
|
||||
main_hbox.call_deferred("add_child", scroll_container)
|
||||
main_hbox.call_deferred("add_child", dev_tools)
|
||||
scroll_container.call_deferred("add_child", website_container)
|
||||
|
||||
background_panel.visible = is_active
|
||||
@@ -215,3 +234,21 @@ func _on_close_button_pressed() -> void:
|
||||
await close_tween.finished
|
||||
tab_closed.emit()
|
||||
queue_free()
|
||||
|
||||
func toggle_dev_tools() -> void:
|
||||
if not dev_tools:
|
||||
return
|
||||
|
||||
dev_tools_visible = not dev_tools_visible
|
||||
dev_tools.visible = dev_tools_visible
|
||||
|
||||
if dev_tools_visible:
|
||||
scroll_container.size_flags_stretch_ratio = 2.0
|
||||
dev_tools.size_flags_stretch_ratio = 1.0
|
||||
else:
|
||||
scroll_container.size_flags_stretch_ratio = 1.0
|
||||
|
||||
func get_dev_tools_console() -> DevToolsConsole:
|
||||
if dev_tools and dev_tools.has_method("get_console"):
|
||||
return dev_tools.get_console()
|
||||
return null
|
||||
|
||||
1
flumi/Scripts/Trace.gd.uid
Normal file
1
flumi/Scripts/Trace.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://8tv4fx7krev1
|
||||
@@ -272,7 +272,7 @@ static func render_new_element(element: HTMLParser.HTMLElement, parent_node: Nod
|
||||
# Create the visual node for the element
|
||||
var element_node = await main_scene.create_element_node(element, dom_parser)
|
||||
if not element_node:
|
||||
LuaPrintUtils.lua_print_direct("Failed to create visual node for element: " + str(element))
|
||||
Trace.trace_log("Failed to create visual node for element: " + str(element))
|
||||
return
|
||||
|
||||
# Set metadata so ul/ol can detect dynamically added li elements
|
||||
|
||||
@@ -10,12 +10,9 @@ static func lua_print(vm: LuauVM) -> int:
|
||||
message_parts.append(value_str)
|
||||
|
||||
var final_message = "\t".join(message_parts)
|
||||
lua_print_direct(final_message)
|
||||
Trace.trace_log(final_message)
|
||||
return 0
|
||||
|
||||
static func lua_print_direct(msg) -> void:
|
||||
print("GURT LOG: ", msg)
|
||||
|
||||
static func lua_value_to_string(vm: LuauVM, index: int) -> String:
|
||||
var lua_type = vm.lua_type(index)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ extends RefCounted
|
||||
|
||||
signal script_completed(result: Dictionary)
|
||||
signal script_error(error: String)
|
||||
signal print_output(message: String)
|
||||
signal print_output(print_data: Dictionary)
|
||||
signal dom_operation_request(operation: Dictionary)
|
||||
|
||||
var lua_thread: Thread
|
||||
@@ -238,23 +238,26 @@ func _print_handler(vm: LuauVM) -> int:
|
||||
var num_args = vm.lua_gettop()
|
||||
|
||||
for i in range(1, num_args + 1):
|
||||
var arg_str = ""
|
||||
if vm.lua_isstring(i):
|
||||
arg_str = vm.lua_tostring(i)
|
||||
elif vm.lua_isnumber(i):
|
||||
arg_str = str(vm.lua_tonumber(i))
|
||||
elif vm.lua_isboolean(i):
|
||||
arg_str = "true" if vm.lua_toboolean(i) else "false"
|
||||
elif vm.lua_isnil(i):
|
||||
arg_str = "nil"
|
||||
var lua_type = vm.lua_type(i)
|
||||
if lua_type == vm.LUA_TTABLE:
|
||||
var table_data = vm.lua_todictionary(i)
|
||||
message_parts.append({
|
||||
"type": "table",
|
||||
"data": table_data
|
||||
})
|
||||
else:
|
||||
arg_str = vm.lua_typename(vm.lua_type(i))
|
||||
|
||||
message_parts.append(arg_str)
|
||||
var value_str = LuaPrintUtils.lua_value_to_string(vm, i)
|
||||
message_parts.append({
|
||||
"type": "primitive",
|
||||
"data": value_str
|
||||
})
|
||||
|
||||
var final_message = "\t".join(message_parts)
|
||||
var print_data = {
|
||||
"parts": message_parts,
|
||||
"count": message_parts.size()
|
||||
}
|
||||
|
||||
call_deferred("_emit_print_output", final_message)
|
||||
call_deferred("_emit_print_output", print_data)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -267,10 +270,35 @@ func _time_sleep_handler(vm: LuauVM) -> int:
|
||||
|
||||
return 0
|
||||
|
||||
func _trace_log_handler(vm: LuauVM) -> int:
|
||||
var message = vm.luaL_checkstring(1)
|
||||
call_deferred("_emit_trace_message", message, "lua")
|
||||
return 0
|
||||
|
||||
func _trace_warning_handler(vm: LuauVM) -> int:
|
||||
var message = vm.luaL_checkstring(1)
|
||||
call_deferred("_emit_trace_message", message, "warning")
|
||||
return 0
|
||||
|
||||
func _trace_error_handler(vm: LuauVM) -> int:
|
||||
var message = vm.luaL_checkstring(1)
|
||||
call_deferred("_emit_trace_message", message, "error")
|
||||
return 0
|
||||
|
||||
func _setup_threaded_gurt_api():
|
||||
lua_vm.lua_pushcallable(_print_handler, "print")
|
||||
lua_vm.lua_setglobal("print")
|
||||
|
||||
# Setup trace functions
|
||||
lua_vm.lua_pushcallable(_trace_log_handler, "_trace_log")
|
||||
lua_vm.lua_setglobal("_trace_log")
|
||||
|
||||
lua_vm.lua_pushcallable(_trace_warning_handler, "_trace_warning")
|
||||
lua_vm.lua_setglobal("_trace_warning")
|
||||
|
||||
lua_vm.lua_pushcallable(_trace_error_handler, "_trace_error")
|
||||
lua_vm.lua_setglobal("_trace_error")
|
||||
|
||||
LuaTimeUtils.setup_time_api(lua_vm)
|
||||
|
||||
lua_vm.lua_getglobal("Time")
|
||||
@@ -281,9 +309,6 @@ func _setup_threaded_gurt_api():
|
||||
|
||||
lua_vm.lua_newtable()
|
||||
|
||||
lua_vm.lua_pushcallable(_print_handler, "gurt.log")
|
||||
lua_vm.lua_setfield(-2, "log")
|
||||
|
||||
lua_vm.lua_pushcallable(_gurt_select_handler, "gurt.select")
|
||||
lua_vm.lua_setfield(-2, "select")
|
||||
|
||||
@@ -357,6 +382,7 @@ func _setup_additional_lua_apis():
|
||||
LuaCrumbsUtils.setup_crumbs_api(lua_vm)
|
||||
LuaRegexUtils.setup_regex_api(lua_vm)
|
||||
LuaURLUtils.setup_url_api(lua_vm)
|
||||
Trace.setup_trace_api(lua_vm)
|
||||
|
||||
func _table_tostring_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
@@ -370,8 +396,8 @@ func _emit_script_completed(result: Dictionary):
|
||||
func _emit_script_error(error: String):
|
||||
script_error.emit(error)
|
||||
|
||||
func _emit_print_output(message: String):
|
||||
print_output.emit(message)
|
||||
func _emit_print_output(print_data: Dictionary):
|
||||
print_output.emit(print_data)
|
||||
|
||||
func _gurt_select_all_handler(vm: LuauVM) -> int:
|
||||
var selector: String = vm.luaL_checkstring(1)
|
||||
@@ -567,3 +593,12 @@ func _create_threaded_interval(interval_id: int, delay_ms: int):
|
||||
timeout_info.timer = timer
|
||||
lua_api.add_child(timer)
|
||||
timer.start()
|
||||
|
||||
func _emit_trace_message(message: String, level: String):
|
||||
match level:
|
||||
"lua", "log":
|
||||
Trace.trace_log(message)
|
||||
"warning":
|
||||
Trace.trace_warning(message)
|
||||
"error":
|
||||
Trace.trace_error(message)
|
||||
|
||||
87
flumi/Scripts/Utils/Lua/Trace.gd
Normal file
87
flumi/Scripts/Utils/Lua/Trace.gd
Normal file
@@ -0,0 +1,87 @@
|
||||
class_name Trace
|
||||
extends RefCounted
|
||||
|
||||
signal log_message(message: String, level: String, timestamp: float)
|
||||
|
||||
enum LogLevel {
|
||||
LOG,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
static var _instance: Trace
|
||||
static var _messages: Array[Dictionary] = []
|
||||
|
||||
static func get_instance() -> Trace:
|
||||
if not _instance:
|
||||
_instance = Trace.new()
|
||||
return _instance
|
||||
|
||||
static func trace_log(message: String) -> void:
|
||||
_emit_message(message, "log")
|
||||
|
||||
static func trace_warning(message: String) -> void:
|
||||
_emit_message(message, "warning")
|
||||
|
||||
static func trace_error(message: String) -> void:
|
||||
_emit_message(message, "error")
|
||||
|
||||
static func _emit_message(message: String, level: String) -> void:
|
||||
var timestamp = Time.get_ticks_msec() / 1000.0
|
||||
var log_entry = {
|
||||
"message": message,
|
||||
"level": level,
|
||||
"timestamp": timestamp
|
||||
}
|
||||
|
||||
_messages.append(log_entry)
|
||||
get_instance().call_deferred("emit_signal", "log_message", message, level, timestamp)
|
||||
|
||||
match level:
|
||||
"log":
|
||||
print("TRACE LOG: ", message)
|
||||
"warning":
|
||||
print("TRACE WARNING: ", message)
|
||||
"error":
|
||||
print("TRACE ERROR: ", message)
|
||||
|
||||
static func get_all_messages() -> Array[Dictionary]:
|
||||
return _messages.duplicate()
|
||||
|
||||
static func clear_messages() -> void:
|
||||
_messages.clear()
|
||||
|
||||
static func _lua_trace_log_handler(vm: LuauVM) -> int:
|
||||
var message = vm.luaL_checkstring(1)
|
||||
vm.lua_getglobal("_trace_log")
|
||||
vm.lua_pushstring(message)
|
||||
vm.lua_call(1, 0)
|
||||
return 0
|
||||
|
||||
static func _lua_trace_warn_handler(vm: LuauVM) -> int:
|
||||
var message = vm.luaL_checkstring(1)
|
||||
vm.lua_getglobal("_trace_warning")
|
||||
vm.lua_pushstring(message)
|
||||
vm.lua_call(1, 0)
|
||||
return 0
|
||||
|
||||
static func _lua_trace_error_handler(vm: LuauVM) -> int:
|
||||
var message = vm.luaL_checkstring(1)
|
||||
vm.lua_getglobal("_trace_error")
|
||||
vm.lua_pushstring(message)
|
||||
vm.lua_call(1, 0)
|
||||
return 0
|
||||
|
||||
static func setup_trace_api(vm: LuauVM) -> void:
|
||||
vm.lua_newtable()
|
||||
|
||||
vm.lua_pushcallable(_lua_trace_log_handler, "trace.log")
|
||||
vm.lua_setfield(-2, "log")
|
||||
|
||||
vm.lua_pushcallable(_lua_trace_warn_handler, "trace.warn")
|
||||
vm.lua_setfield(-2, "warn")
|
||||
|
||||
vm.lua_pushcallable(_lua_trace_error_handler, "trace.error")
|
||||
vm.lua_setfield(-2, "error")
|
||||
|
||||
vm.lua_setglobal("trace")
|
||||
1
flumi/Scripts/Utils/Lua/Trace.gd.uid
Normal file
1
flumi/Scripts/Utils/Lua/Trace.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://k87sfbjp7myx
|
||||
@@ -61,6 +61,16 @@ func _ready():
|
||||
|
||||
call_deferred("render")
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if Input.is_action_just_pressed("DevTools"):
|
||||
_toggle_dev_tools()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
func _toggle_dev_tools() -> void:
|
||||
var active_tab = get_active_tab()
|
||||
if active_tab:
|
||||
active_tab.toggle_dev_tools()
|
||||
|
||||
func resolve_url(href: String) -> String:
|
||||
return URLUtils.resolve_url(current_domain, href)
|
||||
|
||||
@@ -280,12 +290,16 @@ func render_content(html_bytes: PackedByteArray) -> void:
|
||||
parser.register_dom_node(body, target_container)
|
||||
|
||||
var scripts = parser.find_all("script")
|
||||
var lua_api = null
|
||||
if scripts.size() > 0:
|
||||
lua_api = LuaAPI.new()
|
||||
add_child(lua_api)
|
||||
if active_tab:
|
||||
active_tab.lua_apis.append(lua_api)
|
||||
|
||||
var lua_api = LuaAPI.new()
|
||||
add_child(lua_api)
|
||||
if active_tab:
|
||||
active_tab.lua_apis.append(lua_api)
|
||||
|
||||
lua_api.dom_parser = parser
|
||||
|
||||
if lua_api.threaded_vm:
|
||||
lua_api.threaded_vm.dom_parser = parser
|
||||
|
||||
var i = 0
|
||||
if body:
|
||||
@@ -694,3 +708,9 @@ func get_active_website_container() -> Control:
|
||||
if active_tab:
|
||||
return active_tab.website_container
|
||||
return website_container # fallback to original container
|
||||
|
||||
func get_dev_tools_console() -> DevToolsConsole:
|
||||
var active_tab = get_active_tab()
|
||||
if active_tab:
|
||||
return active_tab.get_dev_tools_console()
|
||||
return null
|
||||
|
||||
@@ -72,3 +72,8 @@ FocusSearch={
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":76,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
DevTools={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
local list = gurt.select('#item-list')
|
||||
local counter = 1
|
||||
|
||||
gurt.log('List manipulation script started.')
|
||||
trace.log('List manipulation script started.')
|
||||
|
||||
add_button:on('click', function()
|
||||
local new_item = gurt.create('li', {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
local infoBox = gurt.select('#info-box')
|
||||
local clickCounter = gurt.select('#click-counter')
|
||||
|
||||
gurt.log('Button attribute demo script started.')
|
||||
trace.log('Button attribute demo script started.')
|
||||
|
||||
local clickCount = 0
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
targetButton:on('click', function()
|
||||
clickCount = clickCount + 1
|
||||
clickCounter.text = 'Button clicked ' .. clickCount .. ' times!'
|
||||
gurt.log('Target button clicked! Count:', clickCount)
|
||||
trace.log('Target button clicked! Count:', clickCount)
|
||||
updateStatus()
|
||||
end)
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
enableBtn:on('click', function()
|
||||
targetButton:setAttribute('disabled', '') -- Remove disabled attribute
|
||||
targetButton:setAttribute('data-value', 'enabled')
|
||||
gurt.log('Target button enabled via setAttribute')
|
||||
trace.log('Target button enabled via setAttribute')
|
||||
updateStatus()
|
||||
end)
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
disableBtn:on('click', function()
|
||||
targetButton:setAttribute('disabled', 'true')
|
||||
targetButton:setAttribute('data-value', 'disabled')
|
||||
gurt.log('Target button disabled via setAttribute')
|
||||
trace.log('Target button disabled via setAttribute')
|
||||
updateStatus()
|
||||
end)
|
||||
|
||||
@@ -92,12 +92,12 @@
|
||||
-- Currently disabled, so enable it
|
||||
targetButton:setAttribute('disabled', '')
|
||||
targetButton:setAttribute('data-value', 'toggled-enabled')
|
||||
gurt.log('Target button toggled to enabled state')
|
||||
trace.log('Target button toggled to enabled state')
|
||||
else
|
||||
-- Currently enabled, so disable it
|
||||
targetButton:setAttribute('disabled', 'true')
|
||||
targetButton:setAttribute('data-value', 'toggled-disabled')
|
||||
gurt.log('Target button toggled to disabled state')
|
||||
trace.log('Target button toggled to disabled state')
|
||||
end
|
||||
|
||||
updateStatus()
|
||||
@@ -109,12 +109,12 @@
|
||||
local type = targetButton:getAttribute('type')
|
||||
local dataValue = targetButton:getAttribute('data-value')
|
||||
|
||||
gurt.log('=== BUTTON STATUS CHECK ===')
|
||||
gurt.log('Disabled attribute:', disabled or 'not set')
|
||||
gurt.log('Type attribute:', type or 'not set')
|
||||
gurt.log('Data-value attribute:', dataValue or 'not set')
|
||||
gurt.log('Click count:', clickCount)
|
||||
gurt.log('===========================')
|
||||
trace.log('=== BUTTON STATUS CHECK ===')
|
||||
trace.log('Disabled attribute:', disabled or 'not set')
|
||||
trace.log('Type attribute:', type or 'not set')
|
||||
trace.log('Data-value attribute:', dataValue or 'not set')
|
||||
trace.log('Click count:', clickCount)
|
||||
trace.log('===========================')
|
||||
|
||||
-- Demonstrate style setAttribute
|
||||
local randomColors = {'bg-red-500', 'bg-green-500', 'bg-purple-500', 'bg-orange-500', 'bg-pink-500'}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
gurt.log('Starting comprehensive Canvas API test...')
|
||||
trace.log('Starting comprehensive Canvas API test...')
|
||||
|
||||
-- Test 1: Basic Rectangle and Circle Drawing
|
||||
local basicCanvas = gurt.select("#basic-canvas")
|
||||
@@ -282,7 +282,7 @@
|
||||
}
|
||||
]])
|
||||
|
||||
gurt.log('Canvas API test completed successfully!')
|
||||
trace.log('Canvas API test completed successfully!')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
<postprocess preset="chrome" />
|
||||
|
||||
<script>
|
||||
gurt.log('Script started!')
|
||||
trace.log('Script started!')
|
||||
|
||||
local copyBtn = gurt.select('#copy-text-btn')
|
||||
|
||||
copyBtn:on('click', function()
|
||||
gurt.log('Copy button clicked!')
|
||||
trace.log('Copy button clicked!')
|
||||
Clipboard.write('Hello from GURT!')
|
||||
end)
|
||||
</script>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
local loadImageBtn = gurt.select('#load-image-btn')
|
||||
local imageContainer = gurt.select('#image-container')
|
||||
|
||||
gurt.log('setInterval & Network Image demo script started.')
|
||||
trace.log('setInterval & Network Image demo script started.')
|
||||
|
||||
local logMessages = {}
|
||||
local counter = 0
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
local mouse = gurt.select('#mouse')
|
||||
local btnmouse = gurt.select('#btnmouse')
|
||||
|
||||
gurt.log('Starting Lua script execution...')
|
||||
trace.log('Starting Lua script execution...')
|
||||
|
||||
gurt.body:on('keypress', function(el)
|
||||
typing.text = table.tostring(el)
|
||||
@@ -68,13 +68,13 @@
|
||||
subscription:unsubscribe()
|
||||
end)
|
||||
|
||||
gurt.log('Event listener attached to button with subscription ID')
|
||||
trace.log('Event listener attached to button with subscription ID')
|
||||
else
|
||||
gurt.log('Could not find button or event log element')
|
||||
trace.log('Could not find button or event log element')
|
||||
end
|
||||
|
||||
-- DOM Manipulation Demo
|
||||
gurt.log('Testing DOM manipulation...')
|
||||
trace.log('Testing DOM manipulation...')
|
||||
|
||||
-- Create a new div with styling
|
||||
local new_div = gurt.create('div', { style = 'bg-red-500 p-4 rounded-lg mb-4' })
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
local urlInput = gurt.select('#url-input')
|
||||
local jsonInput = gurt.select('#json-input')
|
||||
|
||||
gurt.log('Network & JSON API demo script started.')
|
||||
trace.log('Network & JSON API demo script started.')
|
||||
|
||||
local logMessages = {}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
local fireDataBtn = gurt.select('#fire-data-btn')
|
||||
local clearLogBtn = gurt.select('#clear-log-btn')
|
||||
|
||||
gurt.log('Signal API demo script started.')
|
||||
trace.log('Signal API demo script started.')
|
||||
|
||||
local logMessages = {}
|
||||
local connectionCount = 0
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
math.randomseed(Time.now())
|
||||
render()
|
||||
|
||||
gurt.log('Snake game initialized!')
|
||||
trace.log('Snake game initialized!')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
local moveBox = gurt.select('#move-box')
|
||||
local comboBox = gurt.select('#combo-box')
|
||||
|
||||
gurt.log('Tween animation demo started!')
|
||||
trace.log('Tween animation demo started!')
|
||||
|
||||
-- Fade Animation
|
||||
gurt.select('#fade-btn'):on('click', function()
|
||||
@@ -143,7 +143,7 @@
|
||||
-- Callback example
|
||||
gurt.select('#callback-btn'):on('click', function()
|
||||
fadeBox:createTween():to('opacity', 0.3):duration(1.0):easing('inout'):callback(function()
|
||||
gurt.log('Fade animation completed!')
|
||||
trace.log('Fade animation completed!')
|
||||
end):play()
|
||||
end)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
local urlInput = gurt.select('#url-input')
|
||||
local messageInput = gurt.select('#message-input')
|
||||
|
||||
gurt.log('WebSocket API demo script started.')
|
||||
trace.log('WebSocket API demo script started.')
|
||||
|
||||
local logMessages = {}
|
||||
local socket = nil
|
||||
|
||||
Reference in New Issue
Block a user