URL UI, history UI
39
README.md
@@ -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)
|
||||
|
||||
1
flumi/Assets/Icons/checkbox_white.svg
Normal 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 |
37
flumi/Assets/Icons/checkbox_white.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/download.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/download.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/ellipsis-vertical.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/ellipsis-vertical.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/external-link.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/external-link.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/hat-glasses.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/hat-glasses.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/history.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/history.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/log-out.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/log-out.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/message-circle-question-mark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/message-circle-question-mark.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/settings.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/settings.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/square.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-icon lucide-square"><rect width="18" height="18" x="3" y="3" rx="2"/></svg>
|
||||
|
After Width: | Height: | Size: 279 B |
37
flumi/Assets/Icons/square.svg.import
Normal 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
|
||||
1
flumi/Assets/Icons/star.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-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 |
37
flumi/Assets/Icons/star.svg.import
Normal 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
|
||||
BIN
flumi/Assets/Inter-VariableFont_opsz,wght.ttf
Normal file
35
flumi/Assets/Inter-VariableFont_opsz,wght.ttf.import
Normal 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={}
|
||||
326
flumi/Scenes/BrowserMenus/history.tscn
Normal 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"]
|
||||
42
flumi/Scenes/BrowserMenus/history_entry.tscn
Normal 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"]
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
198
flumi/Scripts/GurtProtocol.gd
Normal 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>"""
|
||||
1
flumi/Scripts/GurtProtocol.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://clhivwjs3eujk
|
||||
18
flumi/Scripts/OptionButton.gd
Normal 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
|
||||
1
flumi/Scripts/OptionButton.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vjjhljlftlbk
|
||||
@@ -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"):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
1
flumi/Scripts/history.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ektopbvnhfga
|
||||
10
flumi/Scripts/history_entry.gd
Normal 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)
|
||||
1
flumi/Scripts/history_entry.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bw5pr4wrf780h
|
||||
@@ -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
|
||||
|
||||
@@ -39,6 +39,10 @@ folder_colors={
|
||||
"res://Scripts/": "blue"
|
||||
}
|
||||
|
||||
[gui]
|
||||
|
||||
theme/custom_font="uid://fij84uxfqh4h"
|
||||
|
||||
[input]
|
||||
|
||||
NewTab={
|
||||
|
||||