browser history and navigation

This commit is contained in:
Face
2025-09-06 16:58:24 +03:00
parent 6f70032aab
commit 47c7b4bfaa
16 changed files with 592 additions and 137 deletions

View File

@@ -1,11 +1,43 @@
[gd_scene load_steps=15 format=3 uid="uid://cn24pafwdpb1q"]
[ext_resource type="Texture2D" uid="uid://ctpe0lbehepen" path="res://Assets/gurted.svg" id="1_occ3h"]
[ext_resource type="Script" uid="uid://ektopbvnhfga" path="res://Scripts/history.gd" id="1_yn8i4"]
[ext_resource type="PackedScene" uid="uid://3smiker6ni50" path="res://Scenes/BrowserMenus/history_entry.tscn" id="2_a5287"]
[ext_resource type="Texture2D" uid="uid://gq8g7t4s3ryg" path="res://Assets/Icons/x.svg" id="2_ijpe2"]
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="3_yoadi"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_yoadi"]
content_margin_left = 15.0
content_margin_top = 15.0
content_margin_right = 15.0
content_margin_bottom = 15.0
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
corner_radius_top_left = 30
corner_radius_top_right = 30
corner_radius_bottom_right = 30
corner_radius_bottom_left = 30
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8gbba"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8gbba"]
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
expand_margin_left = 40.0
[sub_resource type="Theme" id="Theme_ijpe2"]
LineEdit/styles/focus = SubResource("StyleBoxEmpty_8gbba")
LineEdit/styles/normal = SubResource("StyleBoxFlat_8gbba")
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a5287"]
content_margin_left = 10.0
bg_color = Color(0.219501, 0.219501, 0.219501, 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_ijpe2"]
content_margin_left = 15.0
content_margin_top = 5.0
@@ -43,78 +75,42 @@ corner_radius_top_right = 25
corner_radius_bottom_right = 25
corner_radius_bottom_left = 25
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8gbba"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8gbba"]
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
expand_margin_left = 40.0
[sub_resource type="Theme" id="Theme_ijpe2"]
LineEdit/styles/focus = SubResource("StyleBoxEmpty_8gbba")
LineEdit/styles/normal = SubResource("StyleBoxFlat_8gbba")
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a5287"]
content_margin_left = 10.0
bg_color = Color(0.219501, 0.219501, 0.219501, 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_yn8i4"]
content_margin_left = 15.0
content_margin_top = 15.0
content_margin_right = 15.0
content_margin_bottom = 5.0
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
bg_color = Color(0.154876, 0.154876, 0.154876, 1)
corner_radius_top_left = 15
corner_radius_top_right = 15
corner_radius_bottom_right = 15
corner_radius_bottom_left = 15
[node name="History" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
[node name="PopupPanel" type="PopupPanel"]
initial_position = 1
size = Vector2i(760, 710)
visible = true
theme_override_styles/panel = SubResource("StyleBoxFlat_yoadi")
script = ExtResource("1_yn8i4")
[node name="Control" type="Control" parent="."]
custom_minimum_size = Vector2(400, 100)
layout_mode = 2
[node name="TextureRect" type="TextureRect" parent="Control"]
layout_mode = 1
offset_right = 417.0
offset_bottom = 417.0
scale = Vector2(0.105, 0.105)
texture = ExtResource("1_occ3h")
stretch_mode = 2
[node name="RichTextLabel" type="RichTextLabel" parent="Control"]
layout_mode = 0
offset_left = 50.0
offset_top = 9.0
offset_right = 339.0
offset_bottom = 85.0
theme_override_font_sizes/bold_font_size = 26
bbcode_enabled = true
text = "[b]History[/b]"
[node name="Main" type="VBoxContainer" parent="."]
custom_minimum_size = Vector2(600, 0)
layout_mode = 2
offset_left = 15.0
offset_top = 15.0
offset_right = 745.0
offset_bottom = 695.0
size_flags_horizontal = 4
theme_override_constants/separation = 15
[node name="LineEdit" type="LineEdit" parent="Main"]
custom_minimum_size = Vector2(0, 45)
layout_mode = 2
size_flags_horizontal = 3
theme = SubResource("Theme_ijpe2")
theme_override_styles/normal = SubResource("StyleBoxFlat_a5287")
placeholder_text = "Search history..."
caret_blink = true
[node name="DeleteMenu" type="PanelContainer" parent="Main"]
visible = false
layout_mode = 2
@@ -149,24 +145,13 @@ theme_override_styles/hover = SubResource("StyleBoxFlat_a8gu8")
theme_override_styles/normal = SubResource("StyleBoxFlat_31fx5")
text = "Delete"
[node name="LineEdit" type="LineEdit" parent="Main"]
custom_minimum_size = Vector2(0, 45)
layout_mode = 2
size_flags_horizontal = 3
theme = SubResource("Theme_ijpe2")
theme_override_styles/normal = SubResource("StyleBoxFlat_a5287")
placeholder_text = "Search history..."
caret_blink = true
[node name="HSeparator" type="HSeparator" parent="Main"]
layout_mode = 2
[node name="PanelContainer2" type="PanelContainer" parent="Main"]
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_yn8i4")
[node name="ScrollContainer" type="ScrollContainer" parent="Main/PanelContainer2"]
custom_minimum_size = Vector2(700, 500)
layout_mode = 2
size_flags_vertical = 3
@@ -174,7 +159,6 @@ size_flags_vertical = 3
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
alignment = 1
[node name="HistoryEntry" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
@@ -322,5 +306,3 @@ layout_mode = 2
[node name="HistoryEntry49" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[connection signal="pressed" from="Main/DeleteMenu/HBoxContainer/CancelButton" to="." method="_on_cancel_button_pressed"]

View File

@@ -30,13 +30,23 @@ layout_mode = 2
[node name="TextureRect" type="TextureRect" parent="."]
custom_minimum_size = Vector2(24, 24)
layout_mode = 2
size_flags_horizontal = 0
size_flags_vertical = 4
texture = ExtResource("2_k4hqm")
stretch_mode = 3
expand_mode = 1
stretch_mode = 5
[node name="RichTextLabel2" type="RichTextLabel" parent="."]
custom_minimum_size = Vector2(350, 0)
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
text = "Selection - Google Fonts"
vertical_alignment = 1
[node name="DomainLabel" type="RichTextLabel" parent="."]
custom_minimum_size = Vector2(150, 0)
layout_mode = 2
theme_override_colors/default_color = Color(0.817521, 0.817521, 0.817521, 1)
text = "example.com"
vertical_alignment = 1
[connection signal="toggled" from="CheckBox" to="." method="_on_check_box_toggled"]

View File

@@ -1,11 +1,13 @@
[gd_scene load_steps=26 format=3 uid="uid://cgav3xl2xgupb"]
[gd_scene load_steps=31 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://gq8g7t4s3ryg" path="res://Assets/Icons/x.svg" id="2_pqhy6"]
[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"]
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="6_8muo7"]
[ext_resource type="Script" uid="uid://dh3jdrot4r7m3" path="res://Scripts/NetworkTab.gd" id="7_network"]
[ext_resource type="Script" uid="uid://21a6ds271vmb" path="res://Scripts/DevTools.gd" id="25_devtools"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6hj4c"]
@@ -172,11 +174,38 @@ corner_radius_bottom_left = 8
content_margin_top = 5.0
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
[node name="DevTools" type="VBoxContainer"]
custom_minimum_size = Vector2(495, 400)
size_flags_vertical = 3
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_apu5o"]
[node name="TabContainer" type="TabContainer" parent="."]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dbrn0"]
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_heash"]
bg_color = Color(0.6, 0.6, 0.6, 0)
draw_center = false
[node name="Control" type="Control"]
custom_minimum_size = Vector2(495, 400)
layout_mode = 3
anchors_preset = 0
size_flags_vertical = 3
script = ExtResource("25_devtools")
[node name="DevTools" type="VBoxContainer" parent="."]
custom_minimum_size = Vector2(495, 400)
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 3
theme_override_constants/separation = 0
[node name="TabContainer" type="TabContainer" parent="DevTools"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
size_flags_vertical = 3
@@ -191,7 +220,7 @@ tab_alignment = 1
current_tab = 1
drag_to_rearrange_enabled = true
[node name="Elements" type="Label" parent="TabContainer"]
[node name="Elements" type="Label" parent="DevTools/TabContainer"]
visible = false
layout_mode = 2
text = "Elements tab - Coming soon"
@@ -199,16 +228,16 @@ horizontal_alignment = 1
vertical_alignment = 1
metadata/_tab_index = 0
[node name="Console" type="VBoxContainer" parent="TabContainer"]
[node name="Console" type="VBoxContainer" parent="DevTools/TabContainer"]
layout_mode = 2
script = ExtResource("2_3m6n9")
metadata/_tab_index = 1
[node name="Toolbar" type="HBoxContainer" parent="TabContainer/Console"]
[node name="Toolbar" type="HBoxContainer" parent="DevTools/TabContainer/Console"]
layout_mode = 2
theme_override_constants/separation = 8
[node name="ClearButton" type="Button" parent="TabContainer/Console/Toolbar"]
[node name="ClearButton" type="Button" parent="DevTools/TabContainer/Console/Toolbar"]
custom_minimum_size = Vector2(32, 32)
layout_mode = 2
theme_override_constants/icon_max_width = 20
@@ -219,7 +248,7 @@ theme_override_styles/normal = SubResource("StyleBoxFlat_dderp")
icon = ExtResource("3_6hj4c")
icon_alignment = 1
[node name="LineEdit" type="LineEdit" parent="TabContainer/Console/Toolbar"]
[node name="LineEdit" type="LineEdit" parent="DevTools/TabContainer/Console/Toolbar"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/focus = SubResource("StyleBoxFlat_ko37l")
@@ -227,29 +256,29 @@ theme_override_styles/normal = SubResource("StyleBoxFlat_65n6c")
placeholder_text = "Filter"
right_icon = ExtResource("4_ynqb1")
[node name="Spacer2" type="Control" parent="TabContainer/Console"]
[node name="Spacer2" type="Control" parent="DevTools/TabContainer/Console"]
custom_minimum_size = Vector2(0, 15)
layout_mode = 2
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Console"]
[node name="ScrollContainer" type="ScrollContainer" parent="DevTools/TabContainer/Console"]
custom_minimum_size = Vector2(0, 200)
layout_mode = 2
size_flags_vertical = 3
[node name="LogContainer" type="VBoxContainer" parent="TabContainer/Console/ScrollContainer"]
[node name="LogContainer" type="VBoxContainer" parent="DevTools/TabContainer/Console/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Spacer" type="Control" parent="TabContainer/Console"]
[node name="Spacer" type="Control" parent="DevTools/TabContainer/Console"]
custom_minimum_size = Vector2(0, 15)
layout_mode = 2
[node name="InputContainer" type="HBoxContainer" parent="TabContainer/Console"]
[node name="InputContainer" type="HBoxContainer" parent="DevTools/TabContainer/Console"]
layout_mode = 2
theme_override_constants/separation = 4
[node name="InputLine" type="CodeEdit" parent="TabContainer/Console/InputContainer"]
[node name="InputLine" type="CodeEdit" parent="DevTools/TabContainer/Console/InputContainer"]
clip_contents = false
custom_minimum_size = Vector2(0, 35)
layout_mode = 2
@@ -267,9 +296,9 @@ 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="PositioningTimer" type="Timer" parent="DevTools/TabContainer/Console/InputContainer"]
[node name="Sources" type="Label" parent="TabContainer"]
[node name="Sources" type="Label" parent="DevTools/TabContainer"]
visible = false
layout_mode = 2
text = "Sources tab - Coming soon"
@@ -277,36 +306,36 @@ horizontal_alignment = 1
vertical_alignment = 1
metadata/_tab_index = 2
[node name="Network" type="VBoxContainer" parent="TabContainer"]
[node name="Network" type="VBoxContainer" parent="DevTools/TabContainer"]
visible = false
layout_mode = 2
script = ExtResource("7_network")
metadata/_tab_index = 3
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Network"]
[node name="HBoxContainer" type="HBoxContainer" parent="DevTools/TabContainer/Network"]
layout_mode = 2
[node name="StatusBar" type="HBoxContainer" parent="TabContainer/Network/HBoxContainer"]
[node name="StatusBar" type="HBoxContainer" parent="DevTools/TabContainer/Network/HBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 16
[node name="RequestCount" type="Label" parent="TabContainer/Network/HBoxContainer/StatusBar"]
[node name="RequestCount" type="Label" parent="DevTools/TabContainer/Network/HBoxContainer/StatusBar"]
layout_mode = 2
text = "0 requests"
[node name="Transfer" type="Label" parent="TabContainer/Network/HBoxContainer/StatusBar"]
[node name="Transfer" type="Label" parent="DevTools/TabContainer/Network/HBoxContainer/StatusBar"]
layout_mode = 2
text = "0 B transferred"
[node name="Loaded" type="Label" parent="TabContainer/Network/HBoxContainer/StatusBar"]
[node name="Loaded" type="Label" parent="DevTools/TabContainer/Network/HBoxContainer/StatusBar"]
layout_mode = 2
text = "0 resources loaded"
[node name="Control" type="Control" parent="TabContainer/Network/HBoxContainer"]
[node name="Control" type="Control" parent="DevTools/TabContainer/Network/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="FilterDropdown" type="OptionButton" parent="TabContainer/Network/HBoxContainer"]
[node name="FilterDropdown" type="OptionButton" parent="DevTools/TabContainer/Network/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
theme = ExtResource("6_8muo7")
@@ -339,34 +368,34 @@ popup/item_7/id = 7
popup/item_8/text = "Other"
popup/item_8/id = 8
[node name="HSeparator2" type="HSeparator" parent="TabContainer/Network"]
[node name="HSeparator2" type="HSeparator" parent="DevTools/TabContainer/Network"]
layout_mode = 2
theme_override_constants/separation = 14
[node name="MainContainer" type="HSplitContainer" parent="TabContainer/Network"]
[node name="MainContainer" type="HSplitContainer" parent="DevTools/TabContainer/Network"]
layout_mode = 2
size_flags_vertical = 3
[node name="LeftPanel" type="VBoxContainer" parent="TabContainer/Network/MainContainer"]
[node name="LeftPanel" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer"]
layout_mode = 2
[node name="HeaderRow" type="HBoxContainer" parent="TabContainer/Network/MainContainer/LeftPanel"]
[node name="HeaderRow" type="HBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel"]
custom_minimum_size = Vector2(0, 28)
layout_mode = 2
theme_override_constants/separation = 8
[node name="IconHeader" type="Control" parent="TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
[node name="IconHeader" type="Control" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
custom_minimum_size = Vector2(25, 20)
layout_mode = 2
[node name="NameHeader" type="Label" parent="TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
[node name="NameHeader" type="Label" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
size_flags_horizontal = 3
text = "Name"
vertical_alignment = 1
[node name="StatusHeader" type="Label" parent="TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
[node name="StatusHeader" type="Label" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
@@ -374,7 +403,7 @@ text = "Status"
horizontal_alignment = 1
vertical_alignment = 1
[node name="TypeHeader" type="Label" parent="TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
[node name="TypeHeader" type="Label" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
@@ -382,7 +411,7 @@ text = "Type"
horizontal_alignment = 1
vertical_alignment = 1
[node name="SizeHeader" type="Label" parent="TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
[node name="SizeHeader" type="Label" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
@@ -390,7 +419,7 @@ text = "Size"
horizontal_alignment = 2
vertical_alignment = 1
[node name="TimeHeader" type="Label" parent="TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
[node name="TimeHeader" type="Label" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/HeaderRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
@@ -398,31 +427,31 @@ text = "Time"
horizontal_alignment = 2
vertical_alignment = 1
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Network/MainContainer/LeftPanel"]
[node name="ScrollContainer" type="ScrollContainer" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel"]
layout_mode = 2
size_flags_vertical = 3
[node name="RequestList" type="VBoxContainer" parent="TabContainer/Network/MainContainer/LeftPanel/ScrollContainer"]
[node name="RequestList" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/LeftPanel/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="RightPanel" type="VBoxContainer" parent="TabContainer/Network/MainContainer"]
[node name="RightPanel" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer"]
visible = false
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
size_flags_horizontal = 3
[node name="PanelContainer" type="PanelContainer" parent="TabContainer/Network/MainContainer/RightPanel"]
[node name="PanelContainer" type="PanelContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel"]
layout_mode = 2
size_flags_vertical = 3
theme_override_styles/panel = SubResource("StyleBoxFlat_6hj4c")
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Network/MainContainer/RightPanel/PanelContainer"]
[node name="HBoxContainer" type="HBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 0
[node name="CloseButton" type="Button" parent="TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer"]
[node name="CloseButton" type="Button" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(32, 32)
layout_mode = 2
@@ -432,7 +461,7 @@ theme_override_styles/focus = SubResource("StyleBoxEmpty_xkykt")
text = "✕"
flat = true
[node name="TabContainer" type="TabContainer" parent="TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer"]
[node name="TabContainer" type="TabContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
@@ -445,21 +474,21 @@ theme_override_styles/tab_hovered = SubResource("StyleBoxFlat_8muo7")
theme_override_styles/tab_unselected = SubResource("StyleBoxFlat_xkykt")
current_tab = 0
[node name="Headers" type="VBoxContainer" parent="TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"]
[node name="Headers" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"]
layout_mode = 2
metadata/_tab_index = 0
[node name="Preview" type="VBoxContainer" parent="TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"]
[node name="Preview" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"]
visible = false
layout_mode = 2
metadata/_tab_index = 1
[node name="Response" type="VBoxContainer" parent="TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"]
[node name="Response" type="VBoxContainer" parent="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/TabContainer"]
visible = false
layout_mode = 2
metadata/_tab_index = 2
[node name="Application" type="Label" parent="TabContainer"]
[node name="Application" type="Label" parent="DevTools/TabContainer"]
visible = false
layout_mode = 2
text = "Application tab - Coming soon"
@@ -467,5 +496,20 @@ horizontal_alignment = 1
vertical_alignment = 1
metadata/_tab_index = 4
[connection signal="item_selected" from="TabContainer/Network/HBoxContainer/FilterDropdown" to="TabContainer/Network" method="_on_filter_selected"]
[connection signal="pressed" from="TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/CloseButton" to="TabContainer/Network" method="hide_details_panel"]
[node name="CloseButton" type="Button" parent="."]
layout_mode = 2
offset_left = 9.0
offset_top = 2.0
offset_right = 33.0
offset_bottom = 26.0
theme_override_styles/focus = SubResource("StyleBoxEmpty_apu5o")
theme_override_styles/hover = SubResource("StyleBoxFlat_dbrn0")
theme_override_styles/pressed = SubResource("StyleBoxFlat_heash")
theme_override_styles/normal = SubResource("StyleBoxFlat_heash")
icon = ExtResource("2_pqhy6")
flat = true
icon_alignment = 1
[connection signal="item_selected" from="DevTools/TabContainer/Network/HBoxContainer/FilterDropdown" to="DevTools/TabContainer/Network" method="_on_filter_selected"]
[connection signal="pressed" from="DevTools/TabContainer/Network/MainContainer/RightPanel/PanelContainer/HBoxContainer/CloseButton" to="DevTools/TabContainer/Network" method="hide_details_panel"]
[connection signal="pressed" from="CloseButton" to="." method="_on_close_button_pressed"]

View File

@@ -251,6 +251,7 @@ item_5/id = 5
item_6/text = "Bookmarks (CTRL SHIFT B)"
item_6/icon = ExtResource("17_ueoa1")
item_6/id = 7
item_6/disabled = true
item_7/id = 7
item_7/separator = true
item_8/text = "Help"
@@ -283,9 +284,6 @@ theme_override_constants/separation = 22
[node name="HistoryContainer" parent="VBoxContainer/ScrollContainer" instance=ExtResource("24_3pmx8")]
visible = false
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Panel" type="Panel" parent="."]
z_index = -5
@@ -311,6 +309,9 @@ mouse_filter = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_21xkr")
[connection signal="pressed" from="VBoxContainer/TabContainer/NewTabButton" to="VBoxContainer/TabContainer" method="_on_new_tab_button_pressed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/BackButton" to="." method="_on_back_button_pressed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/ForwardButton" to="." method="_on_forward_button_pressed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/RefreshButton" to="." method="_on_refresh_button_pressed"]
[connection signal="focus_entered" from="VBoxContainer/HBoxContainer/LineEdit" to="." method="_on_search_focus_entered"]
[connection signal="focus_exited" from="VBoxContainer/HBoxContainer/LineEdit" to="." method="_on_search_focus_exited"]
[connection signal="text_submitted" from="VBoxContainer/HBoxContainer/LineEdit" to="." method="_on_search_submitted"]

View File

@@ -0,0 +1,91 @@
extends Node
const HISTORY_FILE_PATH = "user://browser_history.json"
const MAX_HISTORY_ENTRIES = 1000
func get_history_data() -> Array:
var history_file = FileAccess.open(HISTORY_FILE_PATH, FileAccess.READ)
if not history_file:
return []
var json_string = history_file.get_as_text()
history_file.close()
var json = JSON.new()
var parse_result = json.parse(json_string)
if parse_result != OK:
return []
var history_data = json.data
if not history_data is Array:
return []
return history_data
func save_history_data(history_data: Array):
var history_file = FileAccess.open(HISTORY_FILE_PATH, FileAccess.WRITE)
if not history_file:
push_error("Failed to open history file for writing")
return
var json_string = JSON.stringify(history_data)
history_file.store_string(json_string)
history_file.close()
func add_entry(url: String, title: String, icon_url: String = ""):
if url.is_empty():
return
var history_data = get_history_data()
var timestamp = Time.get_datetime_string_from_system()
var existing_index = -1
for i in range(history_data.size()):
if history_data[i].url == url:
existing_index = i
break
var entry = {
"url": url,
"title": title,
"timestamp": timestamp,
"icon_url": icon_url
}
if existing_index >= 0:
history_data.remove_at(existing_index)
history_data.insert(0, entry)
if history_data.size() > MAX_HISTORY_ENTRIES:
history_data = history_data.slice(0, MAX_HISTORY_ENTRIES)
save_history_data(history_data)
func remove_entry(url: String):
var history_data = get_history_data()
for i in range(history_data.size() - 1, -1, -1):
if history_data[i].url == url:
history_data.remove_at(i)
save_history_data(history_data)
func clear_all():
save_history_data([])
func search_history(query: String) -> Array:
var history_data = get_history_data()
var results = []
query = query.to_lower()
for entry in history_data:
var title = entry.get("title", "").to_lower()
var url = entry.get("url", "").to_lower()
if title.contains(query) or url.contains(query):
results.append(entry)
return results

View File

@@ -0,0 +1 @@
uid://c8wiwde042pkx

View File

@@ -0,0 +1,4 @@
extends Control
func _on_close_button_pressed():
Engine.get_main_loop().current_scene._toggle_dev_tools()

View File

@@ -0,0 +1 @@
uid://21a6ds271vmb

View File

@@ -1,10 +1,31 @@
extends Button
const HISTORY = preload("res://Scenes/BrowserMenus/history.tscn")
@onready var tab_container: TabManager = $"../../TabContainer"
@onready var main: Main = $"../../../"
var history_scene: PopupPanel = null
func _on_pressed() -> void:
%OptionsMenu.show()
func _input(_event: InputEvent) -> void:
if _event is InputEventKey and _event.pressed and _event.ctrl_pressed:
if _event.keycode == KEY_N:
if _event.shift_pressed:
# CTRL+SHIFT+N - New incognito window
_on_options_menu_id_pressed(2)
get_viewport().set_input_as_handled()
else:
# CTRL+N - New window
_on_options_menu_id_pressed(1)
get_viewport().set_input_as_handled()
elif _event.keycode == KEY_H:
# CTRL+H - History
_on_options_menu_id_pressed(4)
get_viewport().set_input_as_handled()
func _on_options_menu_id_pressed(id: int) -> void:
if id == 0: # new tab
tab_container.create_tab()
@@ -14,4 +35,21 @@ func _on_options_menu_id_pressed(id: int) -> void:
# TODO: handle incognito
OS.create_process(OS.get_executable_path(), ["--incognito"])
if id == 4: # history
modulate = Constants.SECONDARY_COLOR
show_history()
if id == 10: # exit
get_tree().quit()
func show_history() -> void:
if history_scene == null:
history_scene = HISTORY.instantiate()
history_scene.navigate_to_url.connect(main.navigate_to_url)
main.add_child(history_scene)
history_scene.connect("popup_hide", _on_history_closed)
else:
history_scene.load_history()
history_scene.show()
func _on_history_closed() -> void:
if history_scene:
history_scene.hide()

View File

@@ -41,6 +41,8 @@ var dev_tools_visible: bool = false
var lua_apis: Array[LuaAPI] = []
var current_url: String = ""
var has_content: bool = false
var navigation_history: Array[String] = []
var history_index: int = -1
func _ready():
add_to_group("tabs")
@@ -70,8 +72,11 @@ func update_icon_from_url(icon_url: String) -> void:
if icon_url.is_empty():
const GLOBE_ICON = preload("res://Assets/Icons/globe.svg")
set_icon(GLOBE_ICON)
remove_meta("original_icon_url")
return
set_meta("original_icon_url", icon_url)
var icon_resource = await Network.fetch_image(icon_url)
if is_instance_valid(self) and icon_resource:
@@ -252,3 +257,39 @@ func get_dev_tools_console() -> DevToolsConsole:
if dev_tools and dev_tools.has_method("get_console"):
return dev_tools.get_console()
return null
func add_to_navigation_history(url: String) -> void:
if url.is_empty():
return
# If we're not at the end of history, remove everything after current position
if history_index < navigation_history.size() - 1:
navigation_history = navigation_history.slice(0, history_index + 1)
# Don't add duplicate consecutive entries
if navigation_history.is_empty() or navigation_history[-1] != url:
navigation_history.append(url)
history_index = navigation_history.size() - 1
func can_go_back() -> bool:
return history_index > 0
func can_go_forward() -> bool:
return history_index < navigation_history.size() - 1
func go_back() -> String:
if can_go_back():
history_index -= 1
return navigation_history[history_index]
return ""
func go_forward() -> String:
if can_go_forward():
history_index += 1
return navigation_history[history_index]
return ""
func get_current_history_url() -> String:
if history_index >= 0 and history_index < navigation_history.size():
return navigation_history[history_index]
return ""

View File

@@ -205,6 +205,8 @@ func set_active_tab(index: int) -> void:
main.current_domain = ""
main.search_bar.text = ""
main.search_bar.grab_focus()
main.update_navigation_buttons()
func create_tab() -> void:
var index = tabs.size();

View File

@@ -65,3 +65,21 @@ static func resolve_url(base_url: String, relative_url: String) -> String:
result += "/" + "/".join(final_path_parts)
return result
static func extract_domain(url: String) -> String:
if url.is_empty():
return ""
var clean_url = url
if clean_url.begins_with("gurt://"):
clean_url = clean_url.substr(7)
elif clean_url.begins_with("https://"):
clean_url = clean_url.substr(8)
elif clean_url.begins_with("http://"):
clean_url = clean_url.substr(7)
var slash_pos = clean_url.find("/")
if slash_pos != -1:
clean_url = clean_url.substr(0, slash_pos)
return clean_url

View File

@@ -1,16 +1,20 @@
extends MarginContainer
extends PopupPanel
signal navigate_to_url(url: String)
@onready var history_entry_container: VBoxContainer = $Main/PanelContainer2/ScrollContainer/HistoryEntryContainer
@onready var delete_menu: PanelContainer = $Main/DeleteMenu
@onready var line_edit: LineEdit = $Main/LineEdit
@onready var entries_label: RichTextLabel = $Main/DeleteMenu/HBoxContainer/RichTextLabel
@onready var cancel_button: Button = $Main/DeleteMenu/HBoxContainer/CancelButton
@onready var delete_button: Button = $Main/DeleteMenu/HBoxContainer/DeleteButton
var toggled_entries = []
var history_entry_scene = preload("res://Scenes/BrowserMenus/history_entry.tscn")
func _ready():
for entry in history_entry_container.get_children():
entry.connect("checkbox_toggle", history_toggle.bind(entry))
delete_button.pressed.connect(_on_delete_button_pressed)
line_edit.text_changed.connect(_on_search_text_changed)
load_history()
func history_toggle(toggled: bool, entry) -> void:
print('toggling ', entry, ' to :', toggled)
@@ -37,3 +41,80 @@ func _on_cancel_button_pressed() -> void:
delete_menu.hide()
line_edit.show()
func _on_delete_button_pressed() -> void:
var urls_to_delete = []
for entry in toggled_entries:
if entry.has_meta("history_url"):
urls_to_delete.append(entry.get_meta("history_url"))
for url in urls_to_delete:
remove_history_entry(url)
var entries_to_remove = toggled_entries.duplicate()
toggled_entries.clear()
for entry in entries_to_remove:
history_entry_container.remove_child(entry)
entry.queue_free()
delete_menu.hide()
line_edit.show()
func _on_search_text_changed(search_text: String) -> void:
filter_history_entries(search_text)
func load_history():
var history_data = BrowserHistory.get_history_data()
var existing_entries = history_entry_container.get_children()
var needs_update = existing_entries.size() != history_data.size()
if not needs_update and history_data.size() > 0 and existing_entries.size() > 0:
var first_entry = existing_entries[0]
if first_entry.has_meta("history_url"):
var stored_url = first_entry.get_meta("history_url")
if stored_url != history_data[0].url:
needs_update = true
if needs_update:
clear_displayed_entries()
for entry in history_data:
add_history_entry_to_display(entry.url, entry.title, entry.timestamp, entry.icon_url)
show()
func clear_displayed_entries():
for child in history_entry_container.get_children():
child.queue_free()
func add_history_entry_to_display(url: String, title_: String, timestamp: String, icon_url: String = ""):
var entry_instance = history_entry_scene.instantiate()
history_entry_container.add_child(entry_instance)
entry_instance.setup_entry(url, title_, timestamp, icon_url)
entry_instance.connect("checkbox_toggle", history_toggle.bind(entry_instance))
entry_instance.connect("entry_clicked", _on_entry_clicked)
entry_instance.set_meta("history_url", url)
func filter_history_entries(search_text: String):
if search_text.is_empty():
# Show all entries
for child in history_entry_container.get_children():
child.visible = true
return
# Filter existing entries by showing/hiding them
var query = search_text.to_lower()
for child in history_entry_container.get_children():
if child.has_method("get_title") and child.has_method("get_url"):
var title_ = child.get_title().to_lower()
var url = child.get_url().to_lower()
child.visible = title_.contains(query) or url.contains(query)
else:
child.visible = false
func remove_history_entry(url: String):
BrowserHistory.remove_entry(url)
func _on_entry_clicked(url: String):
navigate_to_url.emit(url)

View File

@@ -1,10 +1,84 @@
extends HBoxContainer
signal checkbox_toggle
signal entry_clicked(url: String)
@onready var check_box: CheckBox = $CheckBox
@onready var time_label: RichTextLabel = $RichTextLabel
@onready var icon: TextureRect = $TextureRect
@onready var title_label: RichTextLabel = $RichTextLabel2
@onready var domain_label: RichTextLabel = $DomainLabel
var entry_url: String = ""
var entry_title: String = ""
func reset() -> void:
check_box.set_pressed_no_signal(false)
func _on_check_box_toggled(toggled_on: bool) -> void:
checkbox_toggle.emit(toggled_on)
func setup_entry(url: String, title: String, timestamp: String, icon_url: String = ""):
entry_url = url
entry_title = title
title_label.text = title if not title.is_empty() else url
var domain = URLUtils.extract_domain(url)
if domain.is_empty():
domain = url
domain_label.text = domain
var datetime_dict = Time.get_datetime_dict_from_datetime_string(timestamp, false)
if datetime_dict.has("hour") and datetime_dict.has("minute"):
var hour = datetime_dict.hour
var minute = datetime_dict.minute
var am_pm = "AM"
if hour == 0:
hour = 12
elif hour > 12:
hour -= 12
am_pm = "PM"
elif hour == 12:
am_pm = "PM"
time_label.text = "%d:%02d%s" % [hour, minute, am_pm]
else:
time_label.text = ""
if not icon_url.is_empty():
_load_icon(icon_url)
else:
const GLOBE_ICON = preload("res://Assets/Icons/globe.svg")
icon.texture = GLOBE_ICON
func _load_icon(icon_url: String):
if icon_url.is_empty():
const GLOBE_ICON = preload("res://Assets/Icons/globe.svg")
icon.texture = GLOBE_ICON
return
icon.texture = null
var icon_resource = await Network.fetch_image(icon_url)
if is_instance_valid(self) and icon_resource:
icon.texture = icon_resource
elif is_instance_valid(self):
const GLOBE_ICON = preload("res://Assets/Icons/globe.svg")
icon.texture = GLOBE_ICON
func get_title() -> String:
return entry_title
func get_url() -> String:
return entry_url
func _ready():
title_label.gui_input.connect(_on_title_clicked)
domain_label.gui_input.connect(_on_title_clicked)
func _on_title_clicked(event: InputEvent):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
entry_clicked.emit(entry_url)

View File

@@ -4,6 +4,9 @@ extends Control
@onready var website_container: Control = %WebsiteContainer
@onready var tab_container: TabManager = $VBoxContainer/TabContainer
@onready var search_bar: LineEdit = $VBoxContainer/HBoxContainer/LineEdit
@onready var back_button: Button = $VBoxContainer/HBoxContainer/BackButton
@onready var forward_button: Button = $VBoxContainer/HBoxContainer/ForwardButton
@onready var refresh_button: Button = $VBoxContainer/HBoxContainer/RefreshButton
const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg")
const AUTO_SIZING_FLEX_CONTAINER = preload("res://Scripts/AutoSizingFlexContainer.gd")
@@ -63,6 +66,7 @@ func _ready():
original_scroll.visible = false
call_deferred("render")
call_deferred("update_navigation_buttons")
func _input(_event: InputEvent) -> void:
if Input.is_action_just_pressed("DevTools"):
@@ -87,7 +91,7 @@ func handle_link_click(meta: Variant) -> void:
else:
OS.shell_open(resolved_url)
func _on_search_submitted(url: String) -> void:
func _on_search_submitted(url: String, add_to_history: bool = true) -> void:
print("Search submitted: ", url)
search_bar.release_focus()
@@ -102,11 +106,11 @@ func _on_search_submitted(url: String) -> void:
if not gurt_url.begins_with("gurt://"):
gurt_url = "gurt://" + gurt_url
await fetch_gurt_content_async(gurt_url, tab, url)
await fetch_gurt_content_async(gurt_url, tab, url, add_to_history)
else:
print("Non-GURT URL entered: ", url)
func fetch_gurt_content_async(gurt_url: String, tab: Tab, original_url: String) -> void:
func fetch_gurt_content_async(gurt_url: String, tab: Tab, original_url: String, add_to_history: bool = true) -> void:
main_navigation_request = NetworkManager.start_request(gurt_url, "GET", false)
main_navigation_request.type = NetworkRequest.RequestType.DOC
network_start_time = Time.get_ticks_msec()
@@ -121,7 +125,7 @@ func fetch_gurt_content_async(gurt_url: String, tab: Tab, original_url: String)
var result = thread.wait_to_finish()
_handle_gurt_result(result, tab, original_url, gurt_url)
_handle_gurt_result(result, tab, original_url, gurt_url, add_to_history)
func _perform_gurt_request_threaded(request_data: Dictionary) -> Dictionary:
var gurt_url: String = request_data.gurt_url
@@ -149,7 +153,7 @@ func _perform_gurt_request_threaded(request_data: Dictionary) -> Dictionary:
return {"success": true, "html_bytes": response.body}
func _handle_gurt_result(result: Dictionary, tab: Tab, original_url: String, gurt_url: String) -> void:
func _handle_gurt_result(result: Dictionary, tab: Tab, original_url: String, gurt_url: String, add_to_history: bool = true) -> void:
if not result.success:
print("GURT request failed: ", result.error)
handle_gurt_error(result.error, tab)
@@ -173,6 +177,11 @@ func _handle_gurt_result(result: Dictionary, tab: Tab, original_url: String, gur
main_navigation_request = null
tab.stop_loading()
if add_to_history:
add_to_history(gurt_url, tab)
else:
update_navigation_buttons()
func handle_gurt_error(error_message: String, tab: Tab) -> void:
var error_html = GurtProtocol.create_error_page(error_message)
@@ -301,6 +310,9 @@ func render_content(html_bytes: PackedByteArray) -> void:
var icon = parser.get_icon()
tab.update_icon_from_url(icon)
if not icon.is_empty():
tab.set_meta("parsed_icon_url", icon)
var body = parser.find_first("body")
if body:
@@ -719,9 +731,12 @@ func reload_current_page() -> void:
if not current_domain.is_empty():
_on_search_submitted(current_domain)
func navigate_to_url(url: String) -> void:
var resolved_url = resolve_url(url)
_on_search_submitted(resolved_url)
func navigate_to_url(url: String, add_to_history: bool = true) -> void:
if url.begins_with("gurt://"):
_on_search_submitted(url, add_to_history)
else:
var resolved_url = resolve_url(url)
_on_search_submitted(resolved_url, add_to_history)
func update_search_bar_from_current_domain() -> void:
if not search_bar.has_focus() and not current_domain.is_empty():
@@ -746,3 +761,54 @@ func get_dev_tools_console() -> DevToolsConsole:
if active_tab:
return active_tab.get_dev_tools_console()
return null
func add_to_history(url: String, tab: Tab, add_to_navigation: bool = true):
if url.is_empty():
return
var title = "New Tab"
var icon_url = ""
if tab:
if add_to_navigation:
tab.add_to_navigation_history(url)
if tab.button and tab.button.text:
title = tab.button.text
if tab.has_meta("parsed_icon_url"):
icon_url = tab.get_meta("parsed_icon_url")
var clean_url = url
if clean_url.begins_with("gurt://"):
clean_url = clean_url.substr(7)
BrowserHistory.add_entry(clean_url, title, icon_url)
update_navigation_buttons()
func _on_back_button_pressed() -> void:
var active_tab = get_active_tab()
if active_tab and active_tab.can_go_back():
var url = active_tab.go_back()
if not url.is_empty():
navigate_to_url(url, false)
func _on_forward_button_pressed() -> void:
var active_tab = get_active_tab()
if active_tab and active_tab.can_go_forward():
var url = active_tab.go_forward()
if not url.is_empty():
navigate_to_url(url, false)
func _on_refresh_button_pressed() -> void:
reload_current_page()
func update_navigation_buttons() -> void:
var active_tab = get_active_tab()
if active_tab:
back_button.disabled = not active_tab.can_go_back()
forward_button.disabled = not active_tab.can_go_forward()
else:
back_button.disabled = true
forward_button.disabled = true

View File

@@ -23,6 +23,7 @@ config/icon="uid://ctpe0lbehepen"
Constants="*res://Scripts/Constants.gd"
Network="*res://Scripts/Network.gd"
NetworkManager="*res://Scripts/NetworkManager.gd"
BrowserHistory="*res://Scripts/BrowserHistory.gd"
[display]