URL UI, history UI

This commit is contained in:
Face
2025-08-12 21:30:41 +03:00
parent fd545885e9
commit c61167b834
48 changed files with 1514 additions and 126 deletions

View File

@@ -19,51 +19,16 @@ TODO:
10. < input type=**datetime** />, essentially a type "date" but with a vertical separator, then `mm | ss | FORMAT` layout for time.
11. **< table >** component. [🔗 Related Godot proposal](https://github.com/godotengine/godot-proposals/issues/97)
12. **< canvas >** component should be theoretically impossible by exposing Godot `_draw()` APIs to Lua.
13. `grid` display property for CSS, using `GridContainer` in Godot.
Issues:
1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing
2. **Tween** API doesn't modify CSS, it operates independently at Godot level.
3. Certain properties like `scale` and `rotate` don't apply to the `active` pseudo-class because they rely on mouse_enter and mouse_exit events
4. `<div style="bg-[#3b82f6] w-[100px] h-[100px] flex hover:scale-110 transition hover:rotate-45">Box</div>` something like this has the "Box" text (presumably the PanelContainer) as the target of the hover, not the div itself (which has the w/h size)
5. font in button doesn't comply with CSS, its the projects default
Notes:
- **< input />** is sort-of inline in normal web. We render it as a block element (new-line).
- A single `RichTextLabel` for inline text tags should stop, we should use invididual ones so it's easier to style and achieve separation through a `vboxcontainer`.
- Fonts use **Flash of Unstyled Text (FOUT)** as opposed to **Flash of Invisible Text (FOIT)**, meaning the text with custom fonts will render with a generic font (sans-serif) while the custom ones downloads.
Supported styles:
- **Font style:**
- `font-bold`
- `font-italic`
- `underline`
- **Font size:**
- `text-xs` → 12
- `text-sm` → 14
- `text-base` → 16
- `text-lg` → 18
- `text-xl` → 20
- `text-2xl` → 24
- `text-3xl` → 30
- `text-4xl` → 36
- `text-5xl` → 48
- `text-6xl` → 60
- **Font family:**
- `font-mono`
- **Text color:**
- `text-[color]`
- **Background color:**
- `bg-[color]`
- **Flexbox**
- `flex` / `inline-flex` (display: flex/inline-flex)
- `flex-row`, `flex-row-reverse`, `flex-col`, `flex-col-reverse` (flex-direction)
- `flex-nowrap`, `flex-wrap`, `flex-wrap-reverse` (flex-wrap)
- `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`, `justify-evenly` (justify-content)
- `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch` (align-items)
- `content-start`, `content-end`, `content-center`, `content-between`, `content-around`, `content-evenly`, `content-stretch` (align-content)
- `gap-{size}`, `row-gap-{size}`, `col-gap-{size}` (gap, row-gap, column-gap)
- `flex-grow-{n}` (flex-grow)
- `flex-shrink-{n}` (flex-shrink)
- `basis-{size}` (flex-basis)
- `self-auto`, `self-start`, `self-end`, `self-center`, `self-stretch`, `self-baseline` (align-self)
- `order-{n}` (order)

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 134 134" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M129.026,35.376l0,62.54c0,17.225 -13.984,31.208 -31.208,31.208l-62.416,0c-17.224,0 -31.208,-13.983 -31.208,-31.208l-0,-62.54c-0,-17.224 13.984,-31.208 31.208,-31.208l62.416,0c17.224,0 31.208,13.984 31.208,31.208Z" style="fill:none;stroke:#fff;stroke-width:8.33px;"/></svg>

After

Width:  |  Height:  |  Size: 746 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://blskvfjswul1d"
path="res://.godot/imported/checkbox_white.svg-3c05c756132e06b6df4f4ae643f84518.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/checkbox_white.svg"
dest_files=["res://.godot/imported/checkbox_white.svg-3c05c756132e06b6df4f4ae643f84518.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

View 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-download-icon lucide-download"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>

After

Width:  |  Height:  |  Size: 332 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cbwitcygwoqdo"
path="res://.godot/imported/download.svg-a21cd5d191a2f0c8e42168f24a4d40ed.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/download.svg"
dest_files=["res://.godot/imported/download.svg-a21cd5d191a2f0c8e42168f24a4d40ed.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

View 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-ellipsis-vertical-icon lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cklatjc4m38dy"
path="res://.godot/imported/ellipsis-vertical.svg-294910634c008df812f7ef47e8a8a214.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/ellipsis-vertical.svg"
dest_files=["res://.godot/imported/ellipsis-vertical.svg-294910634c008df812f7ef47e8a8a214.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

View 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-external-link-icon lucide-external-link"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>

After

Width:  |  Height:  |  Size: 356 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://du24f6em2nqwq"
path="res://.godot/imported/external-link.svg-075235ddf5518da7b64a7c5332224a40.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/external-link.svg"
dest_files=["res://.godot/imported/external-link.svg-075235ddf5518da7b64a7c5332224a40.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

View 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-hat-glasses-icon lucide-hat-glasses"><path d="M14 18a2 2 0 0 0-4 0"/><path d="m19 11-2.11-6.657a2 2 0 0 0-2.752-1.148l-1.276.61A2 2 0 0 1 12 4H8.5a2 2 0 0 0-1.925 1.456L5 11"/><path d="M2 11h20"/><circle cx="17" cy="18" r="3"/><circle cx="7" cy="18" r="3"/></svg>

After

Width:  |  Height:  |  Size: 460 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dwxquqmmd6dqx"
path="res://.godot/imported/hat-glasses.svg-a7dbfb316472ae1702496a78a0ce47e7.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/hat-glasses.svg"
dest_files=["res://.godot/imported/hat-glasses.svg-a7dbfb316472ae1702496a78a0ce47e7.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

View 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-history-icon lucide-history"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/></svg>

After

Width:  |  Height:  |  Size: 336 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bcaoarwrwqbby"
path="res://.godot/imported/history.svg-ce846a8aaa80f3d2f2a595f430c7ab7c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/history.svg"
dest_files=["res://.godot/imported/history.svg-ce846a8aaa80f3d2f2a595f430c7ab7c.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

View 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-log-out-icon lucide-log-out"><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/></svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cilwaxcv5dr1i"
path="res://.godot/imported/log-out.svg-85530e4e6b0167a15c809830d3302ef2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/log-out.svg"
dest_files=["res://.godot/imported/log-out.svg-85530e4e6b0167a15c809830d3302ef2.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

View 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-message-circle-question-mark-icon lucide-message-circle-question-mark"><path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>

After

Width:  |  Height:  |  Size: 479 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c5pr3tb8rwxb8"
path="res://.godot/imported/message-circle-question-mark.svg-6e0198922e95be66aacb1420c83ea704.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/message-circle-question-mark.svg"
dest_files=["res://.godot/imported/message-circle-question-mark.svg-6e0198922e95be66aacb1420c83ea704.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

View 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-settings-icon lucide-settings"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/></svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://mjr3nwamrqon"
path="res://.godot/imported/settings.svg-2aa0f389da6ad0a7e346738ae84fd469.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/settings.svg"
dest_files=["res://.godot/imported/settings.svg-2aa0f389da6ad0a7e346738ae84fd469.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

View 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-square-icon lucide-square"><rect width="18" height="18" x="3" y="3" rx="2"/></svg>

After

Width:  |  Height:  |  Size: 279 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://81l2fi381yub"
path="res://.godot/imported/square.svg-64faf57e7837716ed0ed796cd3df54d8.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/square.svg"
dest_files=["res://.godot/imported/square.svg-64faf57e7837716ed0ed796cd3df54d8.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

View 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-star-icon lucide-star"><path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"/></svg>

After

Width:  |  Height:  |  Size: 602 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://codete2cbsqo2"
path="res://.godot/imported/star.svg-680978d494bec0663c353c2873105a07.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/star.svg"
dest_files=["res://.godot/imported/star.svg-680978d494bec0663c353c2873105a07.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

Binary file not shown.

View File

@@ -0,0 +1,35 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://fij84uxfqh4h"
path="res://.godot/imported/Inter-VariableFont_opsz,wght.ttf-c02350de2ed9338f034e978775e464ff.fontdata"
[deps]
source_file="res://Assets/Inter-VariableFont_opsz,wght.ttf"
dest_files=["res://.godot/imported/Inter-VariableFont_opsz,wght.ttf-c02350de2ed9338f034e978775e464ff.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
hinting=1
subpixel_positioning=4
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

View File

@@ -0,0 +1,326 @@
[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_ijpe2"]
content_margin_left = 15.0
content_margin_top = 5.0
content_margin_right = 15.0
content_margin_bottom = 5.0
bg_color = Color(0.105882, 0.105882, 0.105882, 1)
corner_radius_top_left = 15
corner_radius_top_right = 15
corner_radius_bottom_right = 15
corner_radius_bottom_left = 15
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_yn8i4"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_a8gu8"]
bg_color = Color(0.169245, 0.169245, 0.169245, 1)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.247059, 0.466667, 0.807843, 1)
corner_radius_top_left = 25
corner_radius_top_right = 25
corner_radius_bottom_right = 25
corner_radius_bottom_left = 25
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_31fx5"]
draw_center = false
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(0.247059, 0.466667, 0.807843, 1)
corner_radius_top_left = 25
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)
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
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
size_flags_horizontal = 4
theme_override_constants/separation = 15
[node name="DeleteMenu" type="PanelContainer" parent="Main"]
visible = false
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ijpe2")
[node name="HBoxContainer" type="HBoxContainer" parent="Main/DeleteMenu"]
layout_mode = 2
[node name="CancelButton" type="Button" parent="Main/DeleteMenu/HBoxContainer"]
layout_mode = 2
mouse_default_cursor_shape = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_yn8i4")
icon = ExtResource("2_ijpe2")
flat = true
icon_alignment = 1
[node name="RichTextLabel" type="RichTextLabel" parent="Main/DeleteMenu/HBoxContainer"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
text = "1 selected"
fit_content = true
vertical_alignment = 1
[node name="DeleteButton" type="Button" parent="Main/DeleteMenu/HBoxContainer"]
custom_minimum_size = Vector2(80, 35)
layout_mode = 2
size_flags_horizontal = 10
mouse_default_cursor_shape = 2
theme = ExtResource("3_yoadi")
theme_override_colors/font_color = Color(0.781065, 0.858202, 0.977018, 1)
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"]
layout_mode = 2
size_flags_vertical = 3
[node name="HistoryEntryContainer" type="VBoxContainer" parent="Main/PanelContainer2/ScrollContainer"]
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
[node name="HistoryEntry2" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry3" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry4" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry5" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry6" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry7" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry8" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry9" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry10" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry11" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry12" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry13" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry14" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry15" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry16" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry17" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry18" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry19" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry20" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry21" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry22" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry23" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry24" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry25" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry26" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry27" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry28" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry29" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry30" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry31" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry32" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry33" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry34" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry35" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry36" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry37" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry38" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry39" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry40" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry41" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry42" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry43" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry44" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry45" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry46" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry47" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
layout_mode = 2
[node name="HistoryEntry48" parent="Main/PanelContainer2/ScrollContainer/HistoryEntryContainer" instance=ExtResource("2_a5287")]
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

@@ -0,0 +1,42 @@
[gd_scene load_steps=5 format=3 uid="uid://3smiker6ni50"]
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="1_4plhl"]
[ext_resource type="Script" uid="uid://bw5pr4wrf780h" path="res://Scripts/history_entry.gd" id="1_h5c6k"]
[ext_resource type="Texture2D" uid="uid://blskvfjswul1d" path="res://Assets/Icons/checkbox_white.svg" id="2_h5c6k"]
[ext_resource type="Texture2D" uid="uid://bqpx2lgo0yecb" path="res://Assets/Icons/globe.svg" id="2_k4hqm"]
[node name="HistoryEntry" type="HBoxContainer"]
theme_override_constants/separation = 10
script = ExtResource("1_h5c6k")
[node name="CheckBox" type="CheckBox" parent="."]
layout_mode = 2
theme = ExtResource("1_4plhl")
theme_override_icons/unchecked = ExtResource("2_h5c6k")
flat = true
[node name="RichTextLabel" type="RichTextLabel" parent="."]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
text = "2:00PM"
fit_content = true
horizontal_alignment = 1
vertical_alignment = 1
[node name="Spacer" type="Control" parent="."]
custom_minimum_size = Vector2(50, 0)
layout_mode = 2
[node name="TextureRect" type="TextureRect" parent="."]
custom_minimum_size = Vector2(24, 24)
layout_mode = 2
texture = ExtResource("2_k4hqm")
stretch_mode = 3
[node name="RichTextLabel2" type="RichTextLabel" parent="."]
custom_minimum_size = Vector2(350, 0)
layout_mode = 2
text = "Selection - Google Fonts"
vertical_alignment = 1
[connection signal="toggled" from="CheckBox" to="." method="_on_check_box_toggled"]

View File

@@ -112,10 +112,10 @@ content_margin_top = 15.0
content_margin_right = 15.0
content_margin_bottom = 15.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
corner_radius_top_left = 10
corner_radius_top_right = 10
corner_radius_bottom_right = 10
corner_radius_bottom_left = 10
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ya683"]
content_margin_left = 15.0

View File

@@ -8,6 +8,7 @@ anchors_preset = 10
anchor_right = 1.0
offset_bottom = 19.0
grow_horizontal = 2
size_flags_horizontal = 3
focus_mode = 2
mouse_default_cursor_shape = 1
theme = ExtResource("2_1glvj")
@@ -15,5 +16,6 @@ theme_override_colors/default_color = Color(0, 0, 0, 1)
bbcode_enabled = true
text = "Placeholder"
fit_content = true
vertical_alignment = 1
selection_enabled = true
script = ExtResource("1_pnbfg")

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=27 format=3 uid="uid://bytm7bt2s4ak8"]
[gd_scene load_steps=40 format=3 uid="uid://bytm7bt2s4ak8"]
[ext_resource type="Script" uid="uid://bg5iqnwic1rio" path="res://Scripts/main.gd" id="1_8q3xr"]
[ext_resource type="Texture2D" uid="uid://df1m4j7uxi63v" path="res://Assets/Icons/chevron-down.svg" id="2_6bp64"]
@@ -9,8 +9,21 @@
[ext_resource type="PackedScene" uid="uid://sqhcxhcre081" path="res://Scenes/Tab.tscn" id="4_344ge"]
[ext_resource type="Texture2D" uid="uid://cu4hjoba6etf" path="res://Assets/Icons/rotate-cw.svg" id="5_344ge"]
[ext_resource type="Texture2D" uid="uid://cehbtwq6gq0cn" path="res://Assets/Icons/plus.svg" id="5_ynf5e"]
[ext_resource type="Texture2D" uid="uid://cklatjc4m38dy" path="res://Assets/Icons/ellipsis-vertical.svg" id="10_6iyac"]
[ext_resource type="Script" uid="uid://bgqglerkcylxx" path="res://addons/SmoothScroll/SmoothScrollContainer.gd" id="10_d1ilt"]
[ext_resource type="Script" uid="uid://b7h0k2h2qwlqv" path="res://addons/SmoothScroll/scroll_damper/expo_scroll_damper.gd" id="11_6iyac"]
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="11_ee4r6"]
[ext_resource type="Script" uid="uid://vjjhljlftlbk" path="res://Scripts/OptionButton.gd" id="11_gt3je"]
[ext_resource type="Texture2D" uid="uid://du24f6em2nqwq" path="res://Assets/Icons/external-link.svg" id="12_gt3je"]
[ext_resource type="Texture2D" uid="uid://81l2fi381yub" path="res://Assets/Icons/square.svg" id="13_3pmx8"]
[ext_resource type="Texture2D" uid="uid://dwxquqmmd6dqx" path="res://Assets/Icons/hat-glasses.svg" id="14_u50mg"]
[ext_resource type="Texture2D" uid="uid://bcaoarwrwqbby" path="res://Assets/Icons/history.svg" id="15_cbgmd"]
[ext_resource type="Texture2D" uid="uid://cbwitcygwoqdo" path="res://Assets/Icons/download.svg" id="16_1w6v2"]
[ext_resource type="Texture2D" uid="uid://codete2cbsqo2" path="res://Assets/Icons/star.svg" id="17_ueoa1"]
[ext_resource type="Texture2D" uid="uid://c5pr3tb8rwxb8" path="res://Assets/Icons/message-circle-question-mark.svg" id="18_6vcvc"]
[ext_resource type="Texture2D" uid="uid://mjr3nwamrqon" path="res://Assets/Icons/settings.svg" id="19_7k868"]
[ext_resource type="Texture2D" uid="uid://cilwaxcv5dr1i" path="res://Assets/Icons/log-out.svg" id="20_hpc6h"]
[ext_resource type="PackedScene" uid="uid://cn24pafwdpb1q" path="res://Scenes/BrowserMenus/history.tscn" id="24_3pmx8"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_344ge"]
@@ -49,9 +62,9 @@ corner_radius_bottom_left = 50
bg_color = Color(0.6, 0.6, 0.6, 0)
draw_center = false
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_8gbba"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_u50mg"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8gbba"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cbgmd"]
bg_color = Color(0.168627, 0.168627, 0.168627, 1)
corner_radius_top_left = 15
corner_radius_top_right = 15
@@ -60,8 +73,8 @@ corner_radius_bottom_left = 15
expand_margin_left = 40.0
[sub_resource type="Theme" id="Theme_jjvhh"]
LineEdit/styles/focus = SubResource("StyleBoxEmpty_8gbba")
LineEdit/styles/normal = SubResource("StyleBoxFlat_8gbba")
LineEdit/styles/focus = SubResource("StyleBoxEmpty_u50mg")
LineEdit/styles/normal = SubResource("StyleBoxFlat_cbgmd")
[sub_resource type="Resource" id="Resource_fdnlq"]
script = ExtResource("11_6iyac")
@@ -200,6 +213,59 @@ scale = Vector2(0.85, 0.85)
texture = ExtResource("3_8gbba")
stretch_mode = 5
[node name="OptionsButton" type="Button" parent="VBoxContainer/HBoxContainer"]
custom_minimum_size = Vector2(45, 0)
layout_mode = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_d1ilt")
theme_override_styles/hover = SubResource("StyleBoxFlat_fdnlq")
theme_override_styles/pressed = SubResource("StyleBoxFlat_d1ilt")
theme_override_styles/normal = SubResource("StyleBoxFlat_d1ilt")
icon = ExtResource("10_6iyac")
icon_alignment = 1
script = ExtResource("11_gt3je")
[node name="OptionsMenu" type="PopupMenu" parent="VBoxContainer/HBoxContainer/OptionsButton"]
unique_name_in_owner = true
position = Vector2i(1510, 125)
size = Vector2i(408, 360)
theme = ExtResource("11_ee4r6")
theme_override_constants/v_separation = 10
item_count = 11
item_0/text = "New tab (CTRL T)"
item_0/icon = ExtResource("12_gt3je")
item_0/id = 0
item_1/text = "New window (CTRL N)"
item_1/icon = ExtResource("13_3pmx8")
item_1/id = 1
item_2/text = "New Incognito Window (CTRL SHIFT N)"
item_2/icon = ExtResource("14_u50mg")
item_2/id = 2
item_3/id = 3
item_3/separator = true
item_4/text = "History (CTRL H)"
item_4/icon = ExtResource("15_cbgmd")
item_4/id = 4
item_5/text = "Downloads (CTRL J)"
item_5/icon = ExtResource("16_1w6v2")
item_5/id = 5
item_6/text = "Bookmarks (CTRL SHIFT B)"
item_6/icon = ExtResource("17_ueoa1")
item_6/id = 7
item_7/id = 7
item_7/separator = true
item_8/text = "Help"
item_8/icon = ExtResource("18_6vcvc")
item_8/id = 8
item_9/text = "Settings"
item_9/icon = ExtResource("19_7k868")
item_9/id = 9
item_10/text = "Exit"
item_10/icon = ExtResource("20_hpc6h")
item_10/id = 10
[node name="Control2" type="Control" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
[node name="Spacer3" type="Control" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 2)
layout_mode = 2
@@ -216,12 +282,17 @@ metadata/_custom_type_script = "uid://bgqglerkcylxx"
[node name="WebsiteContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 200)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
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="WebsiteBackground" type="Panel" parent="."]
unique_name_in_owner = true
z_index = -1
@@ -259,3 +330,8 @@ 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="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"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/OptionsButton" to="VBoxContainer/HBoxContainer/OptionsButton" method="_on_pressed"]
[connection signal="id_pressed" from="VBoxContainer/HBoxContainer/OptionsButton/OptionsMenu" to="VBoxContainer/HBoxContainer/OptionsButton" method="_on_options_menu_id_pressed"]

View File

@@ -151,7 +151,7 @@ func get_element_styles_with_inheritance(element: HTMLElement, event: String = "
styles[property] = inline_parsed[property]
# Inherit certain properties from parent elements
var inheritable_properties = ["width", "height", "font-size", "color", "font-family", "cursor"]
var inheritable_properties = ["width", "height", "font-size", "color", "font-family", "cursor", "font-bold", "font-italic", "underline"]
var parent_element = element.parent
while parent_element:
var parent_styles = get_element_styles_internal(parent_element, event)

View File

@@ -28,6 +28,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.dom_operation_request.connect(_handle_dom_operation)
threaded_vm.print_output.connect(_on_print_output)

View File

@@ -2794,4 +2794,108 @@ audio.play()
# Set the active HTML content to use the audio demo
func _ready():
HTML_CONTENT = HTML_CONTENT_AUDIO_TEST
HTML_CONTENT = """<head>
<title>Login</title>
<icon src="https://cdn-icons-png.flaticon.com/512/295/295128.png">
<meta name="theme-color" content="#1b1b1b">
<meta name="description" content="Login to your account">
<font name="roboto" src="https://fonts.gstatic.com/s/roboto/v48/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2" />
<style>
body { bg-[#1b1b1b] text-[#ffffff] font-roboto flex items-center justify-center p-4 }
.login-card { bg-[#2a2a2a] rounded-lg p-8 shadow-2xl mx-0 }
h1 { text-3xl font-bold text-center mb-6 text-[#ffffff] }
input {
bg-[#3b3b3b]
border-none
rounded-md
p-3
w-full
text-[#ffffff]
placeholder:text-[#999999]
outline-none
focus:ring-2
focus:ring-[#5b5b5b]
mb-4
}
button {
bg-[#4ade80]
text-[#1b1b1b]
font-bold
p-3
rounded-md
w-full
hover:bg-[#22c55e]
active:bg-[#15803d]
cursor-pointer
}
a { text-[#4ade80] hover:text-[#22c55e] cursor-pointer }
#log-output { text-white p-4 rounded-md mt-4 font-mono max-h-40 }
</style>
<script>
local submitBtn = gurt.select('#submit')
local username_input = gurt.select('#username')
local password_input = gurt.select('#password')
local log_output = gurt.select('#log-output')
function addLog(message)
gurt.log(message)
log_output.text = log_output.text .. message .. '\\n'
end
submitBtn:on('submit', function(event)
local username = event.data.username
local password = event.data.password
local request_body = JSON.stringify({
username = username,
password = password
})
print(request_body)
local url = 'http://localhost:8080/auth/login'
local headers = {
['Content-Type'] = 'application/json'
}
addLog('Attempting to log in with username: ' .. username)
log_output.text = ''
local response = fetch(url, {
method = 'POST',
headers = headers,
body = request_body
})
addLog('Response Status: ' .. response.status .. ' ' .. response.statusText)
if response:ok() then
addLog('Login successful!')
local jsonData = response:json()
if jsonData then
addLog('Logged in as user: ' .. jsonData.user.username)
addLog('Token: ' .. jsonData.token:sub(1, 20) .. '...')
end
else
addLog('Request failed with status: ' .. response.status)
local error_data = response:text()
addLog('Error response: ' .. error_data)
end
end)
</script>
</head>
<body>
<div style="login-card">
<h1>Login</h1>
<form id="login-form">
<input id="username" type="text" placeholder="Username" required="true" />
<input id="password" type="password" placeholder="Password" required="true" />
<button type="submit" id="submit">Log In</button>
</form>
<p style="text-center mt-4 text-[#999999] text-base">Don't have an account? <a href="#">Register here</a></p>
<p id="log-output" style="min-h-24"></p>
</div>
</body>""".to_utf8_buffer()

View File

@@ -0,0 +1,198 @@
extends RefCounted
class_name GurtProtocol
const DNS_API_URL = "http://localhost:8080"
static func is_gurt_domain(url: String) -> bool:
if url.begins_with("gurt://"):
return true
var parts = url.split(".")
return parts.size() == 2 and not url.contains("://")
static func parse_gurt_domain(url: String) -> Dictionary:
print("Parsing URL: ", url)
var domain_part = url
if url.begins_with("gurt://"):
domain_part = url.substr(7) # Remove "gurt://"
var parts = domain_part.split(".")
if parts.size() != 2:
print("Invalid domain format: ", domain_part)
return {}
print("Parsed domain - name: ", parts[0], ", tld: ", parts[1])
return {
"name": parts[0],
"tld": parts[1],
"display_url": domain_part
}
static func fetch_domain_info(name: String, tld: String) -> Dictionary:
print("Fetching domain info for: ", name, ".", tld)
var http_request = HTTPRequest.new()
var tree = Engine.get_main_loop()
tree.current_scene.add_child(http_request)
http_request.timeout = 5.0
var url = DNS_API_URL + "/domain/" + name + "/" + tld
print("DNS API URL: ", url)
var error = http_request.request(url)
if error != OK:
print("HTTP request failed with error: ", error)
http_request.queue_free()
return {"error": "Failed to make DNS request"}
var response = await http_request.request_completed
http_request.queue_free()
if response[1] == 0 and response[3].size() == 0:
print("DNS API request timed out")
return {"error": "DNS server is not responding"}
var http_code = response[1]
var body = response[3]
print("DNS API response code: ", http_code)
print("DNS API response body: ", body.get_string_from_utf8())
if http_code != 200:
return {"error": "Domain not found or not approved"}
var json = JSON.new()
var parse_result = json.parse(body.get_string_from_utf8())
if parse_result != OK:
print("JSON parse error: ", parse_result)
return {"error": "Invalid JSON response from DNS server"}
print("Domain info retrieved: ", json.data)
return json.data
static func fetch_index_html(ip: String) -> String:
print("Fetching index.html from IP: ", ip)
var http_request = HTTPRequest.new()
var tree = Engine.get_main_loop()
tree.current_scene.add_child(http_request)
http_request.timeout = 5.0
var url = "http://" + ip + "/index.html"
print("Fetching from URL: ", url)
var error = http_request.request(url)
if error != OK:
print("HTTP request to IP failed with error: ", error)
http_request.queue_free()
return ""
var response = await http_request.request_completed
http_request.queue_free()
if response[1] == 0 and response[3].size() == 0:
print("Index.html request timed out")
return ""
var http_code = response[1]
var body = response[3]
print("IP response code: ", http_code)
if http_code != 200:
print("Failed to fetch index.html, HTTP code: ", http_code)
return ""
var html_content = body.get_string_from_utf8()
print("Successfully fetched HTML content (", html_content.length(), " characters)")
return html_content
static func handle_gurt_domain(url: String) -> Dictionary:
print("Handling GURT domain: ", url)
var parsed = parse_gurt_domain(url)
if parsed.is_empty():
return {"error": "Invalid domain format. Use: domain.tld", "html": create_error_page("Invalid domain format. Use: domain.tld")}
var domain_info = await fetch_domain_info(parsed.name, parsed.tld)
if domain_info.has("error"):
return {"error": domain_info.error, "html": create_error_page(domain_info.error)}
var html_content = await fetch_index_html(domain_info.ip)
if html_content.is_empty():
var error_msg = "Failed to fetch index.html from " + domain_info.ip
return {"error": error_msg, "html": create_error_page(error_msg)}
return {"html": html_content, "display_url": parsed.display_url}
static func get_error_type(error_message: String) -> Dictionary:
if "DNS server is not responding" in error_message or "Domain not found" in error_message:
return {"code": "ERR_NAME_NOT_RESOLVED", "title": "This site can't be reached", "icon": "🌐"}
elif "timeout" in error_message.to_lower() or "timed out" in error_message.to_lower():
return {"code": "ERR_CONNECTION_TIMED_OUT", "title": "This site can't be reached", "icon": ""}
elif "Failed to fetch" in error_message or "HTTP request failed" in error_message:
return {"code": "ERR_CONNECTION_REFUSED", "title": "This site can't be reached", "icon": "🚫"}
elif "Invalid domain format" in error_message:
return {"code": "ERR_INVALID_URL", "title": "This page isn't working", "icon": "⚠️"}
else:
return {"code": "ERR_UNKNOWN", "title": "Something went wrong", "icon": ""}
static func create_error_page(error_message: String) -> String:
var error_info = get_error_type(error_message)
return """<head>
<title>""" + error_info.title + """ - GURT</title>
<meta name="theme-color" content="#f8f9fa">
<style>
body { bg-[#ffffff] text-[#202124] font-sans p-0 m-0 }
.error-container { flex flex-col items-center justify-center max-w-[600px] mx-auto px-6 text-center }
.error-icon { text-6xl mb-6 opacity-60 w-32 h-32 }
.error-title { text-[#202124] text-2xl font-normal mb-4 line-height-1.3 }
.error-subtitle { text-[#5f6368] text-base mb-6 line-height-1.4 }
.error-code { bg-[#f8f9fa] text-[#5f6368] px-3 py-2 rounded-md font-mono text-sm inline-block mb-6 }
.suggestions { text-left max-w-[400px] w-[500px] }
.suggestion-title { text-[#202124] text-lg font-normal mb-3 }
.suggestion-list { text-[#5f6368] text-sm line-height-1.6 }
.suggestion-item { mb-2 pl-4 relative }
.suggestion-item:before { content-"" absolute left-0 top-0 text-[#5f6368] }
.retry-button { bg-[#1a73e8] text-[#ffffff] px-6 py-3 rounded-md font-medium text-sm hover:bg-[#1557b0] active:bg-[#1246a0] cursor-pointer border-none mt-4 }
.details-section { mt-8 pt-6 border-t border-[#e8eaed] }
.details-toggle { text-[#1a73e8] text-sm cursor-pointer hover:underline }
.details-content { bg-[#f8f9fa] text-[#5f6368] text-xs font-mono p-4 rounded-md mt-3 text-left display-none }
</style>
<script>
gurt.select("#reload"):on("click", function()
gurt.location.reload()
end)
</script>
</head>
<body>
<div style="error-container">
<p style="error-icon">""" + error_info.icon + """</p>
<h1 style="error-title">""" + error_info.title + """</h1>
<p style="error-subtitle">""" + error_message + """</p>
<div style="error-code">""" + error_info.code + """</div>
<div style="suggestions">
<h2 style="suggestion-title">Try:</h2>
<ul style="suggestion-list">
<li style="suggestion-item">Checking if the domain is correctly registered</li>
<li style="suggestion-item">Verifying your DNS server is running</li>
<li style="suggestion-item">Checking your internet connection</li>
</ul>
</div>
<button style="retry-button" id="reload">Reload</button>
</div>
</body>"""

View File

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

View File

@@ -0,0 +1,18 @@
extends Button
@onready var tab_container: TabManager = $"../../TabContainer"
@onready var website_background: Panel = %WebsiteBackground
func _on_pressed() -> void:
%OptionsMenu.show()
func _on_options_menu_id_pressed(id: int) -> void:
if id == 0: # new tab
tab_container.create_tab()
if id == 1: # new window
OS.create_process(OS.get_executable_path(), [])
if id == 2: # new ingonito window
# TODO: handle incognito
OS.create_process(OS.get_executable_path(), ["--incognito"])
if id == 4: # history
website_background.modulate = Constants.SECONDARY_COLOR

View File

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

View File

@@ -318,16 +318,23 @@ static func apply_margin_wrapper(node: Control, styles: Dictionary) -> Control:
var margin_container = MarginContainer.new()
margin_container.name = "MarginWrapper_" + node.name
# Copy size flags from the original node
margin_container.size_flags_horizontal = node.size_flags_horizontal
margin_container.size_flags_vertical = node.size_flags_vertical
var has_explicit_width = styles.has("width")
var has_explicit_height = styles.has("height")
if has_explicit_width:
margin_container.size_flags_horizontal = node.size_flags_horizontal
else:
margin_container.size_flags_horizontal = node.size_flags_horizontal
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if has_explicit_height:
margin_container.size_flags_vertical = node.size_flags_vertical
else:
margin_container.size_flags_vertical = node.size_flags_vertical
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
apply_margin_styles_to_container(margin_container, styles)
# Reset the original node's size flags since they're now handled by the wrapper
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
# Handle reparenting properly
var original_parent = node.get_parent()
if original_parent:
@@ -542,9 +549,22 @@ static func parse_radius(radius_str: String) -> int:
static func apply_font_to_label(label: RichTextLabel, font_resource: Font) -> void:
label.add_theme_font_override("normal_font", font_resource)
label.add_theme_font_override("bold_font", font_resource)
label.add_theme_font_override("italics_font", font_resource)
label.add_theme_font_override("bold_italics_font", font_resource)
var bold_font = SystemFont.new()
bold_font.font_names = font_resource.font_names if font_resource is SystemFont else ["Arial"]
bold_font.font_weight = 700 # Bold weight
label.add_theme_font_override("bold_font", bold_font)
var italic_font = SystemFont.new()
italic_font.font_names = font_resource.font_names if font_resource is SystemFont else ["Arial"]
italic_font.font_italic = true
label.add_theme_font_override("italics_font", italic_font)
var bold_italic_font = SystemFont.new()
bold_italic_font.font_names = font_resource.font_names if font_resource is SystemFont else ["Arial"]
bold_italic_font.font_weight = 700 # Bold weight
bold_italic_font.font_italic = true
label.add_theme_font_override("bold_italics_font", bold_italic_font)
static func apply_font_to_button(button: Button, styles: Dictionary) -> void:
if styles.has("font-family"):

View File

@@ -47,6 +47,10 @@ func set_icon(new_icon: Texture) -> void:
icon.rotation = 0
func update_icon_from_url(icon_url: String) -> void:
if icon_url.is_empty():
stop_loading()
return
const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg")
loading_tween = create_tween()
@@ -68,9 +72,7 @@ func update_icon_from_url(icon_url: String) -> void:
# Only update if tab still exists
if is_instance_valid(self):
set_icon(icon_resource)
if loading_tween:
loading_tween.kill()
loading_tween = null
stop_loading()
func _on_button_mouse_entered() -> void:
mouse_over_tab = true
@@ -82,6 +84,27 @@ func _on_button_mouse_exited() -> void:
if is_active: return
gradient_texture.texture = TAB_GRADIENT_DEFAULT
func start_loading() -> void:
const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg")
stop_loading()
loading_tween = create_tween()
set_icon(LOADER_CIRCLE)
loading_tween.set_loops()
icon.pivot_offset = Vector2(11.5, 11.5)
loading_tween.tween_method(func(angle):
if !is_instance_valid(icon):
if loading_tween: loading_tween.kill()
return
icon.rotation = angle
, 0.0, TAU, 1.0)
func stop_loading() -> void:
if loading_tween:
loading_tween.kill()
loading_tween = null
func _exit_tree():
if loading_tween:
loading_tween.kill()

View File

@@ -189,10 +189,23 @@ func apply_padding_to_stylebox(style_box: StyleBoxFlat, styles: Dictionary) -> v
func apply_size_and_flags(ctrl: Control, width: Variant, height: Variant) -> void:
if width != null or height != null:
ctrl.custom_minimum_size = Vector2(
width if width != null else 0,
height if height != null else 0
)
var new_width = 0
var new_height = 0
if width != null:
if SizingUtils.is_percentage(width):
new_width = SizingUtils.calculate_percentage_size(width, SizingUtils.DEFAULT_VIEWPORT_WIDTH)
else:
new_width = width
if height != null:
if SizingUtils.is_percentage(height):
new_height = SizingUtils.calculate_percentage_size(height, SizingUtils.DEFAULT_VIEWPORT_HEIGHT)
else:
new_height = height
ctrl.custom_minimum_size = Vector2(new_width, new_height)
if width != null:
ctrl.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
if height != null:

View File

@@ -404,10 +404,22 @@ func apply_input_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
if active_child:
if width or height:
# Explicit sizing from CSS
var new_child_size = Vector2(
width if width else active_child.custom_minimum_size.x,
height if height else max(active_child.custom_minimum_size.y, active_child.size.y)
)
var new_width = active_child.custom_minimum_size.x
var new_height = max(active_child.custom_minimum_size.y, active_child.size.y)
if width:
if SizingUtils.is_percentage(width):
new_width = SizingUtils.calculate_percentage_size(width, SizingUtils.DEFAULT_VIEWPORT_WIDTH)
else:
new_width = width
if height:
if SizingUtils.is_percentage(height):
new_height = SizingUtils.calculate_percentage_size(height, SizingUtils.DEFAULT_VIEWPORT_HEIGHT)
else:
new_height = height
var new_child_size = Vector2(new_width, new_height)
active_child.custom_minimum_size = new_child_size

View File

@@ -7,9 +7,38 @@ func init(element: HTMLParser.HTMLElement, parser: HTMLParser) -> void:
# Allow mouse events to pass through to parent containers for hover effects while keeping text selection
mouse_filter = Control.MOUSE_FILTER_PASS
# NOTE: estimate width/height because FlexContainer removes our anchor preset (sets 0 width)
var plain_text = element.get_collapsed_text()
var estimated_height = 30
var estimated_width = min(200, max(100, plain_text.length() * 12))
autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
custom_minimum_size = Vector2(estimated_width, estimated_height)
call_deferred("_auto_resize_to_content")
size_flags_horizontal = Control.SIZE_EXPAND_FILL
set_anchors_and_offsets_preset(Control.PRESET_TOP_WIDE)
func _auto_resize_to_content():
if not is_inside_tree():
await tree_entered
var min_width = 20
var max_width = 800
var min_height = 30
fit_content = true
var original_autowrap = autowrap_mode
autowrap_mode = TextServer.AUTOWRAP_OFF
await get_tree().process_frame
var natural_width = size.x
var desired_width = clampf(natural_width, min_width, max_width)
autowrap_mode = original_autowrap
await get_tree().process_frame
var content_height = get_content_height()
var explicit_height = custom_minimum_size.y if custom_minimum_size.y > 0 else null
var final_height = explicit_height if explicit_height != null else max(content_height, min_height)
custom_minimum_size = Vector2(desired_width, final_height)
queue_redraw()

39
flumi/Scripts/history.gd Normal file
View File

@@ -0,0 +1,39 @@
extends MarginContainer
@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
var toggled_entries = []
func _ready():
for entry in history_entry_container.get_children():
entry.connect("checkbox_toggle", history_toggle.bind(entry))
func history_toggle(toggled: bool, entry) -> void:
print('toggling ', entry, ' to :', toggled)
if toggled:
toggled_entries.append(entry)
else:
toggled_entries.remove_at(toggled_entries.find(entry))
entries_label.text = str(toggled_entries.size()) + " selected"
if toggled_entries.size() != 0:
delete_menu.show()
line_edit.hide()
else:
delete_menu.hide()
line_edit.show()
func _on_cancel_button_pressed() -> void:
var entries_to_reset = toggled_entries.duplicate()
toggled_entries.clear()
for entry in entries_to_reset:
entry.reset()
delete_menu.hide()
line_edit.show()

View File

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

View File

@@ -0,0 +1,10 @@
extends HBoxContainer
signal checkbox_toggle
@onready var check_box: CheckBox = $CheckBox
func reset() -> void:
check_box.set_pressed_no_signal(false)
func _on_check_box_toggled(toggled_on: bool) -> void:
checkbox_toggle.emit(toggled_on)

View File

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

View File

@@ -4,6 +4,8 @@ extends Control
@onready var website_container: Control = %WebsiteContainer
@onready var website_background: Control = %WebsiteBackground
@onready var tab_container: TabManager = $VBoxContainer/TabContainer
@onready var search_bar: LineEdit = $VBoxContainer/HBoxContainer/LineEdit
const LOADER_CIRCLE = preload("res://Assets/Icons/loader-circle.svg")
const AUTO_SIZING_FLEX_CONTAINER = preload("res://Scripts/AutoSizingFlexContainer.gd")
@@ -53,6 +55,8 @@ func _ready():
DisplayServer.window_set_min_size(MIN_SIZE)
get_viewport().size_changed.connect(_on_viewport_size_changed)
call_deferred("render")
func _on_viewport_size_changed():
recalculate_percentage_elements(website_container)
@@ -64,7 +68,49 @@ func recalculate_percentage_elements(node: Node):
for child in node.get_children():
recalculate_percentage_elements(child)
var current_domain = "" # Store current domain for display
func _on_search_submitted(url: String) -> void:
print("Search submitted: ", url)
if GurtProtocol.is_gurt_domain(url):
print("Processing as GURT domain")
var tab = tab_container.tabs[tab_container.active_tab]
tab.start_loading()
var result = await GurtProtocol.handle_gurt_domain(url)
if result.has("error"):
print("GURT domain error: ", result.error)
const GLOBE_ICON = preload("res://Assets/Icons/globe.svg")
tab.stop_loading()
tab.set_icon(GLOBE_ICON)
var html_bytes = result.html.to_utf8_buffer()
render_content(html_bytes)
if result.has("display_url"):
current_domain = result.display_url
if not search_bar.has_focus():
search_bar.text = current_domain
else:
print("Non-GURT URL entered: ", url)
func _on_search_focus_entered() -> void:
if not current_domain.is_empty():
search_bar.text = "gurt://" + current_domain
func _on_search_focus_exited() -> void:
if not current_domain.is_empty():
search_bar.text = current_domain
func render() -> void:
render_content(Constants.HTML_CONTENT)
func render_content(html_bytes: PackedByteArray) -> void:
# Clear existing content
for child in website_container.get_children():
child.queue_free()
@@ -73,8 +119,6 @@ func render() -> void:
FontManager.clear_fonts()
FontManager.set_refresh_callback(refresh_fonts)
var html_bytes = Constants.HTML_CONTENT
var parser: HTMLParser = HTMLParser.new(html_bytes)
var parse_result = parser.parse()
@@ -109,57 +153,59 @@ func render() -> void:
add_child(lua_api)
var i = 0
while i < body.children.size():
var element: HTMLParser.HTMLElement = body.children[i]
if should_group_as_inline(element):
# Create an HBoxContainer for consecutive inline elements
var inline_elements: Array[HTMLParser.HTMLElement] = []
while i < body.children.size() and should_group_as_inline(body.children[i]):
inline_elements.append(body.children[i])
i += 1
var hbox = HBoxContainer.new()
hbox.add_theme_constant_override("separation", 4)
for inline_element in inline_elements:
var inline_node = await create_element_node(inline_element, parser)
if inline_node:
# Input elements register their own DOM nodes in their init() function
if inline_element.tag_name not in ["input", "textarea", "select", "button", "audio"]:
parser.register_dom_node(inline_element, inline_node)
safe_add_child(hbox, inline_node)
# Handle hyperlinks for all inline elements
if contains_hyperlink(inline_element) and inline_node is RichTextLabel:
inline_node.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
else:
print("Failed to create inline element node: ", inline_element.tag_name)
safe_add_child(website_container, hbox)
continue
var element_node = await create_element_node(element, parser)
if element_node:
# Input elements register their own DOM nodes in their init() function
if element.tag_name not in ["input", "textarea", "select", "button", "audio"]:
parser.register_dom_node(element, element_node)
if body:
while i < body.children.size():
var element: HTMLParser.HTMLElement = body.children[i]
# ul/ol handle their own adding
if element.tag_name != "ul" and element.tag_name != "ol":
safe_add_child(website_container, element_node)
if should_group_as_inline(element):
# Create an HBoxContainer for consecutive inline elements
var inline_elements: Array[HTMLParser.HTMLElement] = []
# Handle hyperlinks for all elements
if contains_hyperlink(element):
if element_node is RichTextLabel:
element_node.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
elif element_node.has_method("get") and element_node.get("rich_text_label"):
element_node.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
else:
print("Couldn't parse unsupported HTML tag \"%s\"" % element.tag_name)
i += 1
while i < body.children.size() and should_group_as_inline(body.children[i]):
inline_elements.append(body.children[i])
i += 1
var hbox = HBoxContainer.new()
hbox.add_theme_constant_override("separation", 4)
for inline_element in inline_elements:
var inline_node = await create_element_node(inline_element, parser)
if inline_node:
# Input elements register their own DOM nodes in their init() function
if inline_element.tag_name not in ["input", "textarea", "select", "button", "audio"]:
parser.register_dom_node(inline_element, inline_node)
safe_add_child(hbox, inline_node)
# Handle hyperlinks for all inline elements
if contains_hyperlink(inline_element) and inline_node is RichTextLabel:
inline_node.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
else:
print("Failed to create inline element node: ", inline_element.tag_name)
safe_add_child(website_container, hbox)
continue
var element_node = await create_element_node(element, parser)
if element_node:
# Input elements register their own DOM nodes in their init() function
if element.tag_name not in ["input", "textarea", "select", "button", "audio"]:
parser.register_dom_node(element, element_node)
# ul/ol handle their own adding
if element.tag_name != "ul" and element.tag_name != "ol":
safe_add_child(website_container, element_node)
if contains_hyperlink(element):
if element_node is RichTextLabel:
element_node.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
elif element_node.has_method("get") and element_node.get("rich_text_label"):
element_node.rich_text_label.meta_clicked.connect(func(meta): OS.shell_open(str(meta)))
else:
print("Couldn't parse unsupported HTML tag \"%s\"" % element.tag_name)
i += 1
if scripts.size() > 0 and lua_api:
parser.process_scripts(lua_api, null)
@@ -381,6 +427,9 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
var p_node = P.instantiate()
p_node.init(element, parser)
var div_styles = parser.get_element_styles_with_inheritance(element, "", [])
StyleManager.apply_styles_to_label(p_node, div_styles, element, parser)
var container_for_children = node
if node is PanelContainer and node.get_child_count() > 0:
container_for_children = node.get_child(0) # The VBoxContainer inside

View File

@@ -39,6 +39,10 @@ folder_colors={
"res://Scripts/": "blue"
}
[gui]
theme/custom_font="uid://fij84uxfqh4h"
[input]
NewTab={