From 99f17dc42cae8a93d076de224eed5e1047aec861 Mon Sep 17 00:00:00 2001
From: Face <69168154+face-hh@users.noreply.github.com>
Date: Tue, 19 Aug 2025 18:27:25 +0300
Subject: [PATCH] user domains API, .value on input, fix flex sizing
---
README.md | 1 -
dns/README.md | 54 ++++-
dns/frontend/dashboard.html | 52 +----
dns/frontend/dashboard.lua | 197 ++---------------
dns/frontend/index.html | 1 +
dns/frontend/register.html | 136 ++++++++++++
dns/frontend/register.lua | 257 +++++++++++++++++++++++
dns/src/gurt_server.rs | 3 +
dns/src/gurt_server/models.rs | 16 ++
dns/src/gurt_server/routes.rs | 52 ++++-
flumi/Scenes/Tags/input.tscn | 46 +---
flumi/Scripts/AutoSizingFlexContainer.gd | 2 +-
flumi/Scripts/B9/HTMLParser.gd | 2 +-
flumi/Scripts/B9/Lua.gd | 10 +-
flumi/Scripts/StyleManager.gd | 59 ++----
flumi/Scripts/Utils/FlexUtils.gd | 3 +-
flumi/Scripts/Utils/Lua/DOM.gd | 119 ++++++++++-
flumi/Scripts/main.gd | 51 +++--
18 files changed, 705 insertions(+), 356 deletions(-)
create mode 100644 dns/frontend/register.html
create mode 100644 dns/frontend/register.lua
diff --git a/README.md b/README.md
index 2826b27..eb9a709 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,6 @@ Issues:
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. `
Box
` 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
-6. Flex containers, ironically enough, make the page unresponsive. This happens because of our custom `AutoSizingFlexContainer.gd` script, which aims to set a Godot UI size to the flex containers based on their content. However, they don't get resized when the window is resized, leading to unresponsiveness. The fact that we're setting the `custom_minimum_size` is not the root cause, but rather the fact that the script doesn't update the size when the window is resized - or, more likely, I just don't understand how flexbox works.
Notes:
- **< input />** is sort-of inline in normal web. We render it as a block element (new-line).
diff --git a/dns/README.md b/dns/README.md
index b52773d..eb9494e 100644
--- a/dns/README.md
+++ b/dns/README.md
@@ -19,6 +19,7 @@ This is a Domain Management API built with Rust (Actix Web) and PostgreSQL. It p
- [GET /auth/me](#get-authme)
- [POST /auth/invite](#post-authinvite)
- [POST /auth/redeem-invite](#post-authredeem-invite)
+ - [GET /auth/domains](#get-authdomains) 🔒
- [Domain Endpoints](#domain-endpoints)
- [GET /](#get-)
- [POST /domain](#post-domain) 🔒
@@ -127,6 +128,50 @@ Redeem an invite code to get 3 additional domain registrations. Requires authent
}
```
+### GET /auth/domains 🔒
+
+Get all domains owned by the authenticated user, including their status. Requires `Authorization: Bearer ` header.
+
+**Query Parameters:**
+- `page` - Page number (default: 1)
+- `limit` - Items per page (default: 100, max: 1000)
+
+**Response:**
+```json
+{
+ "domains": [
+ {
+ "name": "myawesome",
+ "tld": "dev",
+ "ip": "192.168.1.100",
+ "status": "approved",
+ "denial_reason": null
+ },
+ {
+ "name": "pending",
+ "tld": "fr",
+ "ip": "10.0.0.1",
+ "status": "pending",
+ "denial_reason": null
+ },
+ {
+ "name": "rejected",
+ "tld": "mf",
+ "ip": "172.16.0.1",
+ "status": "denied",
+ "denial_reason": "Invalid IP address"
+ }
+ ],
+ "page": 1,
+ "limit": 100
+}
+```
+
+**Status Values:**
+- `pending` - Domain is awaiting approval
+- `approved` - Domain has been approved and is active
+- `denied` - Domain was rejected (see `denial_reason` for details)
+
## Domain Endpoints
### GET /
@@ -159,15 +204,6 @@ Submit a domain for approval. Requires authentication and consumes one registrat
}
```
-**Response:**
-```json
-{
- "message": "Domain registration submitted for approval",
- "domain": "myawesome.dev",
- "status": "pending"
-}
-```
-
**Error Responses:**
- `401 Unauthorized` - Missing or invalid JWT token
- `400 Bad Request` - No registrations remaining, invalid domain, or offensive name
diff --git a/dns/frontend/dashboard.html b/dns/frontend/dashboard.html
index 4e683c1..a1b31a4 100644
--- a/dns/frontend/dashboard.html
+++ b/dns/frontend/dashboard.html
@@ -26,15 +26,15 @@
}
.primary-btn {
- p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#dc2626] text-white
+ p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#dc2626] text-white w-32 h-12
}
.success-btn {
- p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#ef4444] text-white
+ p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#ef4444] text-white w-32 h-12
}
.danger-btn {
- p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#b91c1c] text-white
+ p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#b91c1c] text-white w-32 h-12
}
.secondary-btn {
@@ -42,7 +42,7 @@
}
.warning-btn {
- p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#dc2626] text-white
+ p-3 rounded-lg font-medium cursor-pointer transition-colors bg-[#dc2626] text-white w-32 h-12
}
.form-group {
@@ -58,7 +58,7 @@
}
.stats-card {
- bg-[#1f1f1f] p-4 rounded-lg border border-[#dc2626]
+ p-4 rounded-lg border border-[#dc2626]
}
.domain-item {
@@ -98,48 +98,10 @@
-
-
-
Register New Domain
-
-
-
-
-
-
-
-
-
Invite System
-
Create invite codes to share with friends, or redeem codes to get more domain
- registrations.
-
-
Placeholder
-
-
-
Create Invite
-
-
-
-
Redeem Invite
-
-
+
+
-
diff --git a/dns/frontend/dashboard.lua b/dns/frontend/dashboard.lua
index 41f0931..92734ea 100644
--- a/dns/frontend/dashboard.lua
+++ b/dns/frontend/dashboard.lua
@@ -1,16 +1,12 @@
local user = nil
local domains = {}
-local tlds = {}
local authToken = nil
local userInfo = gurt.select('#user-info')
local domainsList = gurt.select('#domains-list')
-local tldSelector = gurt.select('#tld-selector')
local loadingElement = gurt.select('#tld-loading')
-local displayElement = gurt.select('#invite-code-display')
-local options
-displayElement:hide()
+local options
local function showError(elementId, message)
local element = gurt.select('#' .. elementId)
@@ -29,57 +25,17 @@ local function updateUserInfo()
userInfo.text = 'Welcome, ' .. user.username .. '!'
end
-local function renderTLDSelector()
- loadingElement:remove()
-
- tldSelector.text = ''
- local i = 1
- local total = #tlds
- local intervalId
-
- intervalId = gurt.setInterval(function()
- if i > total then
- gurt.clearInterval(intervalId)
- return
- end
-
- local tld = tlds[i]
- local option = gurt.create('button', {
- text = '.' .. tld,
- style = 'tld-option',
- ['data-tld'] = tld
- })
-
- tldSelector:append(option)
-
- option:on('click', function()
- -- Clear previous selection
- if not options then
- options = gurt.selectAll('.tld-option')
- end
-
- for j = 1, #options do
- if options[j].classList:contains('tld-selected') then
- options[j].classList:remove('tld-selected')
- end
- end
-
- -- Select this option
- option.classList:add('tld-selected')
- end)
- i = i + 1
- end, 16)
-end
-
local function renderDomains()
local loadingElement = gurt.select('#domains-loading')
- loadingElement:remove()
+ if loadingElement then
+ loadingElement:remove()
+ end
domainsList.text = ''
if #domains == 0 then
local emptyMessage = gurt.create('div', {
- text = 'No domains registered yet. Submit your first domain below!',
+ text = 'No domains registered yet. Click "New" to register your first domain!',
style = 'text-center text-[#6b7280] py-8'
})
domainsList:append(emptyMessage)
@@ -91,7 +47,7 @@ local function renderDomains()
style = 'domain-item'
})
- local domainInfo = gurt.create('div', {})
+ local domainInfo = gurt.create('div', { style = 'w-full' })
local domainName = gurt.create('div', {
text = domain.name .. '.' .. domain.tld,
@@ -103,8 +59,14 @@ local function renderDomains()
style = 'text-[#6b7280]'
})
+ local domainStatus = gurt.create('div', {
+ text = 'Status: ' .. (domain.status or 'Unknown'),
+ style = 'text-[#6b7280]'
+ })
+
domainInfo:append(domainName)
domainInfo:append(domainIP)
+ domainInfo:append(domainStatus)
local actions = gurt.create('div', {
style = 'flex gap-2'
@@ -143,7 +105,7 @@ end
local function loadDomains()
print('Loading domains...')
- local response = fetch('gurt://localhost:8877/domains?page=1&size=100', {
+ local response = fetch('gurt://localhost:8877/auth/domains?page=1&size=100', {
headers = {
Authorization = 'Bearer ' .. authToken
}
@@ -159,19 +121,6 @@ local function loadDomains()
end
end
-local function loadTLDs()
- print('Loading available TLDs...')
- local response = fetch('gurt://localhost:8877/tlds')
-
- if response:ok() then
- tlds = response:json()
- print('Loaded ' .. #tlds .. ' TLDs')
- renderTLDSelector()
- else
- print('Failed to load TLDs: ' .. response:text())
- end
-end
-
local function checkAuth()
authToken = gurt.crumbs.get("auth_token")
@@ -188,7 +137,6 @@ local function checkAuth()
print('Authentication successful for user: ' .. user.username)
updateUserInfo()
loadDomains()
- loadTLDs()
else
print('Token invalid, redirecting to login...')
gurt.crumbs.delete('auth_token')
@@ -206,126 +154,13 @@ local function logout()
gurt.location.goto("../")
end
-local function submitDomain(name, tld, ip)
- hideError('domain-error')
- print('Submitting domain: ' .. name .. '.' .. tld)
-
- local response = fetch('gurt://localhost:8877/domain', {
- method = 'POST',
- headers = {
- ['Content-Type'] = 'application/json',
- Authorization = 'Bearer ' .. authToken
- },
- body = JSON.stringify({ name = name, tld = tld, ip = ip })
- })
-
- if response:ok() then
- local data = response:json()
- print('Domain submitted successfully: ' .. data.domain)
-
- -- Update user registrations remaining
- user.registrations_remaining = user.registrations_remaining - 1
- updateUserInfo()
-
- -- Clear form
- gurt.select('#domain-name').text = ''
- gurt.select('#domain-ip').text = ''
-
- -- Refresh domains list
- loadDomains()
- else
- local error = response:text()
- showError('domain-error', 'Domain submission failed: ' .. error)
- print('Domain submission failed: ' .. error)
- end
-end
-
-local function createInvite()
- print('Creating invite code...')
- local response = fetch('gurt://localhost:8877/auth/invite', {
- method = 'POST',
- headers = {
- Authorization = 'Bearer ' .. authToken
- }
- })
-
- if response:ok() then
- local data = response:json()
- local inviteCode = data.invite_code
- displayElement.text = 'Invite code: ' .. inviteCode .. ' (copied to clipboard)'
- displayElement:show()
- Clipboard.write(inviteCode)
- print('Invite code created and copied to clipboard: ' .. inviteCode)
- else
- print('Failed to create invite: ' .. response:text())
- end
-end
-
-local function redeemInvite(code)
- hideError('redeem-error')
- print('Redeeming invite code: ' .. code)
-
- local response = fetch('gurt://localhost:8877/auth/redeem-invite', {
- method = 'POST',
- headers = {
- ['Content-Type'] = 'application/json',
- Authorization = 'Bearer ' .. authToken
- },
- body = JSON.stringify({ invite_code = code })
- })
-
- if response:ok() then
- local data = response:json()
- print('Invite redeemed: +' .. data.registrations_added .. ' registrations')
-
- -- Update user info
- user.registrations_remaining = user.registrations_remaining + data.registrations_added
- updateUserInfo()
-
- -- Clear form
- gurt.select('#invite-code-input').text = ''
- else
- local error = response:text()
- showError('redeem-error', 'Failed to redeem invite: ' .. error)
- print('Failed to redeem invite: ' .. error)
- end
+local function goToRegister()
+ gurt.location.goto("/register.html")
end
-- Event handlers
gurt.select('#logout-btn'):on('click', logout)
-
-gurt.select('#submit-domain-btn'):on('click', function()
- local name = gurt.select('#domain-name').text
- local ip = gurt.select('#domain-ip').text
- local selectedTLD = gurt.select('.tld-selected')
-
- if not name or name == '' then
- showError('domain-error', 'Domain name is required')
- return
- end
-
- if not ip or ip == '' then
- showError('domain-error', 'IP address is required')
- return
- end
-
- if not selectedTLD then
- showError('domain-error', 'Please select a TLD')
- return
- end
-
- local tld = selectedTLD:getAttribute('data-tld')
- submitDomain(name, tld, ip)
-end)
-
-gurt.select('#create-invite-btn'):on('click', createInvite)
-
-gurt.select('#redeem-invite-btn'):on('click', function()
- local code = gurt.select('#invite-code-input').text
- if code and code ~= '' then
- redeemInvite(code)
- end
-end)
+gurt.select('#new-btn'):on('click', goToRegister)
-- Initialize
print('Dashboard initialized')
diff --git a/dns/frontend/index.html b/dns/frontend/index.html
index a323f49..161e15e 100644
--- a/dns/frontend/index.html
+++ b/dns/frontend/index.html
@@ -38,6 +38,7 @@
#log-output { text-white p-4 rounded-md mt-4 font-mono max-h-40 }
+
diff --git a/dns/frontend/register.html b/dns/frontend/register.html
new file mode 100644
index 0000000..8ee9a88
--- /dev/null
+++ b/dns/frontend/register.html
@@ -0,0 +1,136 @@
+
+ Register Domain
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
+
+
+
+
+
+
+
+
Register New Domain
+
+
+
+
+
+
+
+
+
Invite System
+
Create invite codes to share with friends, or redeem codes to get more domain registrations.
+
+
Placeholder
+
+
+
Create Invite
+
+
+
+
Redeem Invite
+
+
+
+
+
+
+
+
+
diff --git a/dns/frontend/register.lua b/dns/frontend/register.lua
new file mode 100644
index 0000000..55f6448
--- /dev/null
+++ b/dns/frontend/register.lua
@@ -0,0 +1,257 @@
+local user = nil
+local tlds = {}
+local authToken = nil
+
+local userInfo = gurt.select('#user-info')
+local tldSelector = gurt.select('#tld-selector')
+local loadingElement = gurt.select('#tld-loading')
+local displayElement = gurt.select('#invite-code-display')
+local remainingElement = gurt.select('#remaining')
+
+local options
+
+displayElement:hide()
+
+local function showError(elementId, message)
+ local element = gurt.select('#' .. elementId)
+
+ element.text = message
+ element.classList:remove('hidden')
+end
+
+local function hideError(elementId)
+ local element = gurt.select('#' .. elementId)
+
+ element.classList:add('hidden')
+end
+
+local function updateUserInfo()
+ userInfo.text = 'Welcome, ' .. user.username .. '!'
+ remainingElement.text = 'Register New Domain (' .. user.registrations_remaining .. ' remaining)'
+end
+
+local function renderTLDSelector()
+ loadingElement:remove()
+
+ tldSelector.text = ''
+ local i = 1
+ local total = #tlds
+ local intervalId
+
+ intervalId = gurt.setInterval(function()
+ if i > total then
+ gurt.clearInterval(intervalId)
+ return
+ end
+
+ local tld = tlds[i]
+ local option = gurt.create('button', {
+ text = '.' .. tld,
+ style = 'tld-option',
+ ['data-tld'] = tld
+ })
+
+ tldSelector:append(option)
+
+ option:on('click', function()
+ -- Clear previous selection
+ if not options then
+ options = gurt.selectAll('.tld-option')
+ end
+
+ for j = 1, #options do
+ if options[j].classList:contains('tld-selected') then
+ options[j].classList:remove('tld-selected')
+ end
+ end
+
+ -- Select this option
+ option.classList:add('tld-selected')
+ end)
+ i = i + 1
+ end, 16)
+end
+
+local function loadTLDs()
+ print('Loading available TLDs...')
+ local response = fetch('gurt://localhost:8877/tlds')
+
+ if response:ok() then
+ tlds = response:json()
+ print('Loaded ' .. #tlds .. ' TLDs')
+ renderTLDSelector()
+ else
+ print('Failed to load TLDs: ' .. response:text())
+ end
+end
+
+local function checkAuth()
+ authToken = gurt.crumbs.get("auth_token")
+
+ if authToken then
+ print('Found auth token, checking validity...')
+ local response = fetch('gurt://localhost:8877/auth/me', {
+ headers = {
+ Authorization = 'Bearer ' .. authToken
+ }
+ })
+
+ if response:ok() then
+ user = response:json()
+ print('Authentication successful for user: ' .. user.username)
+ updateUserInfo()
+ loadTLDs()
+ else
+ print('Token invalid, redirecting to login...')
+ gurt.crumbs.delete('auth_token')
+ gurt.location.goto('../')
+ end
+ else
+ print('No auth token found, redirecting to login...')
+ gurt.location.goto('../')
+ end
+end
+
+local function logout()
+ gurt.crumbs.delete('auth_token')
+ print('Logged out successfully')
+ gurt.location.goto("../")
+end
+
+local function goToDashboard()
+ gurt.location.goto("/dashboard.html")
+end
+
+local function submitDomain(name, tld, ip)
+ hideError('domain-error')
+ print('Submitting domain: ' .. name .. '.' .. tld)
+
+ local response = fetch('gurt://localhost:8877/domain', {
+ method = 'POST',
+ headers = {
+ ['Content-Type'] = 'application/json',
+ Authorization = 'Bearer ' .. authToken
+ },
+ body = JSON.stringify({ name = name, tld = tld, ip = ip })
+ })
+
+ if response:ok() then
+ print('Domain submitted successfully.')
+
+ -- Update user registrations remaining
+ user.registrations_remaining = user.registrations_remaining - 1
+ updateUserInfo()
+
+ -- Clear form
+ gurt.select('#domain-name').text = ''
+ gurt.select('#domain-ip').text = ''
+
+ -- Redirect to dashboard
+ gurt.location.goto('/dashboard.html')
+ else
+ local error = response:text()
+ showError('domain-error', 'Domain submission failed: ' .. error)
+ print('Domain submission failed: ' .. error)
+ end
+end
+
+local function createInvite()
+ print('Creating invite code...')
+ local response = fetch('gurt://localhost:8877/auth/invite', {
+ method = 'POST',
+ headers = {
+ Authorization = 'Bearer ' .. authToken
+ }
+ })
+
+ if response:ok() then
+ local data = response:json()
+ local inviteCode = data.invite_code
+ displayElement.text = 'Invite code: ' .. inviteCode .. ' (copied to clipboard)'
+ displayElement:show()
+ Clipboard.write(inviteCode)
+ print('Invite code created and copied to clipboard: ' .. inviteCode)
+ else
+ print('Failed to create invite: ' .. response:text())
+ end
+end
+
+local function redeemInvite(code)
+ hideError('redeem-error')
+ print('Redeeming invite code: ' .. code)
+
+ local response = fetch('gurt://localhost:8877/auth/redeem-invite', {
+ method = 'POST',
+ headers = {
+ ['Content-Type'] = 'application/json',
+ Authorization = 'Bearer ' .. authToken
+ },
+ body = JSON.stringify({ invite_code = code })
+ })
+
+ if response:ok() then
+ local data = response:json()
+ print('Invite redeemed: +' .. data.registrations_added .. ' registrations')
+
+ -- Update user info
+ user.registrations_remaining = user.registrations_remaining + data.registrations_added
+ updateUserInfo()
+
+ -- Clear form
+ gurt.select('#invite-code-input').text = ''
+ else
+ local error = response:text()
+ showError('redeem-error', 'Failed to redeem invite: ' .. error)
+ print('Failed to redeem invite: ' .. error)
+ end
+end
+
+-- Event handlers
+gurt.select('#logout-btn'):on('click', logout)
+gurt.select('#dashboard-btn'):on('click', goToDashboard)
+
+gurt.select('#submit-domain-btn'):on('click', function()
+ local name = gurt.select('#domain-name').value
+ local ip = gurt.select('#domain-ip').value
+ local selectedTLD = gurt.select('.tld-selected')
+
+ print('Submit domain button clicked')
+ print('Input name:', name)
+ print('Input IP:', ip)
+ print('Selected TLD element:', selectedTLD)
+
+ if not name or name == '' then
+ print('Validation failed: Domain name is required')
+ showError('domain-error', 'Domain name is required')
+ return
+ end
+
+ if not ip or ip == '' then
+ print('Validation failed: IP address is required')
+ showError('domain-error', 'IP address is required')
+ return
+ end
+
+ if not selectedTLD then
+ print('Validation failed: No TLD selected')
+ showError('domain-error', 'Please select a TLD')
+ return
+ end
+
+ local tld = selectedTLD:getAttribute('data-tld')
+ print('Submitting domain with name:', name, 'tld:', tld, 'ip:', ip)
+ submitDomain(name, tld, ip)
+end)
+
+gurt.select('#create-invite-btn'):on('click', createInvite)
+
+gurt.select('#redeem-invite-btn'):on('click', function()
+ local code = gurt.select('#invite-code-input').text
+ if code and code ~= '' then
+ redeemInvite(code)
+ end
+end)
+
+-- Initialize
+print('Register page initialized')
+checkAuth()
diff --git a/dns/src/gurt_server.rs b/dns/src/gurt_server.rs
index 12dc527..9031e4b 100644
--- a/dns/src/gurt_server.rs
+++ b/dns/src/gurt_server.rs
@@ -93,6 +93,7 @@ enum HandlerType {
CreateDomain,
UpdateDomain,
DeleteDomain,
+ GetUserDomains,
}
impl GurtHandler for AppHandler {
@@ -128,6 +129,7 @@ impl GurtHandler for AppHandler {
HandlerType::RedeemInvite => handle_authenticated!(ctx, app_state, auth_routes::redeem_invite),
HandlerType::CreateDomainInvite => handle_authenticated!(ctx, app_state, auth_routes::create_domain_invite),
HandlerType::RedeemDomainInvite => handle_authenticated!(ctx, app_state, auth_routes::redeem_domain_invite),
+ HandlerType::GetUserDomains => handle_authenticated!(ctx, app_state, routes::get_user_domains),
HandlerType::CreateDomain => {
// Check rate limit first
if let Some(ref rate_limit_state) = rate_limit_state {
@@ -205,6 +207,7 @@ pub async fn start(cli: crate::Cli) -> std::io::Result<()> {
.route(Route::post("/auth/redeem-invite"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::RedeemInvite })
.route(Route::post("/auth/create-domain-invite"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::CreateDomainInvite })
.route(Route::post("/auth/redeem-domain-invite"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::RedeemDomainInvite })
+ .route(Route::get("/auth/domains"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::GetUserDomains })
.route(Route::post("/domain"), AppHandler { app_state: app_state.clone(), rate_limit_state: Some(rate_limit_state), handler_type: HandlerType::CreateDomain })
.route(Route::put("/domain/*"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::UpdateDomain })
.route(Route::delete("/domain/*"), AppHandler { app_state: app_state.clone(), rate_limit_state: None, handler_type: HandlerType::DeleteDomain });
diff --git a/dns/src/gurt_server/models.rs b/dns/src/gurt_server/models.rs
index c9acf5c..43c6d61 100644
--- a/dns/src/gurt_server/models.rs
+++ b/dns/src/gurt_server/models.rs
@@ -101,3 +101,19 @@ pub(crate) struct DomainList {
pub(crate) domain: String,
pub(crate) taken: bool,
}
+
+#[derive(Debug, Serialize)]
+pub(crate) struct UserDomain {
+ pub(crate) name: String,
+ pub(crate) tld: String,
+ pub(crate) ip: String,
+ pub(crate) status: String,
+ pub(crate) denial_reason: Option,
+}
+
+#[derive(Serialize)]
+pub(crate) struct UserDomainResponse {
+ pub(crate) domains: Vec,
+ pub(crate) page: u32,
+ pub(crate) limit: u32,
+}
diff --git a/dns/src/gurt_server/routes.rs b/dns/src/gurt_server/routes.rs
index 8b2e7b3..739708a 100644
--- a/dns/src/gurt_server/routes.rs
+++ b/dns/src/gurt_server/routes.rs
@@ -39,7 +39,7 @@ pub(crate) async fn create_logic(domain: Domain, user_id: i32, app: &AppState) -
}
let existing_count: i64 = sqlx::query_scalar(
- "SELECT COUNT(*) FROM domains WHERE name = ? AND tld = ?"
+ "SELECT COUNT(*) FROM domains WHERE name = $1 AND tld = $2"
)
.bind(&domain.name)
.bind(&domain.tld)
@@ -306,6 +306,56 @@ pub(crate) async fn delete_domain(ctx: &ServerContext, app_state: AppState, clai
Ok(GurtResponse::ok().with_string_body("Domain deleted successfully"))
}
+pub(crate) async fn get_user_domains(ctx: &ServerContext, app_state: AppState, claims: Claims) -> Result {
+ // Parse pagination from query parameters
+ let path = ctx.path();
+ let query_params = if let Some(query_start) = path.find('?') {
+ let query_string = &path[query_start + 1..];
+ parse_query_string(query_string)
+ } else {
+ HashMap::new()
+ };
+
+ let page = query_params.get("page")
+ .and_then(|p| p.parse::().ok())
+ .unwrap_or(1)
+ .max(1);
+
+ let page_size = query_params.get("limit")
+ .and_then(|l| l.parse::().ok())
+ .unwrap_or(100)
+ .clamp(1, 1000);
+
+ let offset = (page - 1) * page_size;
+
+ let domains: Vec = sqlx::query_as::<_, Domain>(
+ "SELECT id, name, tld, ip, user_id, status, denial_reason, created_at FROM domains WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3"
+ )
+ .bind(claims.user_id)
+ .bind(page_size as i64)
+ .bind(offset as i64)
+ .fetch_all(&app_state.db)
+ .await
+ .map_err(|_| GurtError::invalid_message("Database error"))?;
+
+ let response_domains: Vec = domains.into_iter().map(|domain| {
+ UserDomain {
+ name: domain.name,
+ tld: domain.tld,
+ ip: domain.ip,
+ status: domain.status.unwrap_or_else(|| "pending".to_string()),
+ denial_reason: domain.denial_reason,
+ }
+ }).collect();
+
+ let response = UserDomainResponse {
+ domains: response_domains,
+ page,
+ limit: page_size,
+ };
+
+ Ok(GurtResponse::ok().with_json_body(&response)?)
+}
#[derive(serde::Serialize)]
struct Error {
diff --git a/flumi/Scenes/Tags/input.tscn b/flumi/Scenes/Tags/input.tscn
index 9f3a2a0..d947683 100644
--- a/flumi/Scenes/Tags/input.tscn
+++ b/flumi/Scenes/Tags/input.tscn
@@ -66,77 +66,55 @@ SpinBox/styles/down_background_pressed = SubResource("StyleBoxEmpty_xxc4c")
SpinBox/styles/up_background_hovered = SubResource("StyleBoxEmpty_unki5")
SpinBox/styles/up_background_pressed = SubResource("StyleBoxEmpty_3wmat")
-[node name="input" type="Control"]
-layout_mode = 3
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-offset_right = -1791.0
-offset_bottom = -996.0
-grow_horizontal = 2
-grow_vertical = 2
-size_flags_horizontal = 3
-size_flags_vertical = 0
+[node name="VBoxContainer" type="VBoxContainer"]
+offset_right = 40.0
+offset_bottom = 40.0
script = ExtResource("1_input")
[node name="LineEdit" type="LineEdit" parent="."]
visible = false
-layout_mode = 1
-offset_right = 200.0
-offset_bottom = 35.0
+layout_mode = 2
theme = ExtResource("2_theme")
placeholder_text = "Enter text..."
caret_blink = true
[node name="CheckBox" type="CheckBox" parent="."]
visible = false
-layout_mode = 0
-offset_right = 31.0
-offset_bottom = 31.0
+layout_mode = 2
theme = ExtResource("2_theme")
flat = true
[node name="RadioButton" type="CheckBox" parent="."]
visible = false
-layout_mode = 0
-offset_right = 31.0
-offset_bottom = 31.0
+layout_mode = 2
theme = ExtResource("2_theme")
theme_override_constants/icon_max_width = 24
button_group = SubResource("ButtonGroup_06us3")
flat = true
[node name="ColorPickerButton" type="ColorPickerButton" parent="."]
-layout_mode = 0
-offset_right = 83.0
-offset_bottom = 35.0
+layout_mode = 2
toggle_mode = false
[node name="DateButton" parent="." instance=ExtResource("3_a88g6")]
visible = false
-layout_mode = 0
+layout_mode = 2
[node name="HSlider" type="HSlider" parent="."]
visible = false
-layout_mode = 0
-offset_right = 200.0
-offset_bottom = 35.0
+layout_mode = 2
theme = ExtResource("2_theme")
value = 50.0
[node name="SpinBox" type="SpinBox" parent="."]
visible = false
-layout_mode = 0
-offset_right = 94.5625
-offset_bottom = 35.0
+layout_mode = 2
theme = SubResource("Theme_poopw")
min_value = -99999.0
max_value = 99999.0
[node name="FileContainer" type="HBoxContainer" parent="."]
-layout_mode = 0
-offset_right = 300.0
-offset_bottom = 35.0
+layout_mode = 2
[node name="FileButton" type="Button" parent="FileContainer"]
custom_minimum_size = Vector2(100, 0)
@@ -159,5 +137,3 @@ ok_button_text = "Open"
file_mode = 0
access = 2
use_native_dialog = true
-
-[connection signal="popup_closed" from="ColorPickerButton" to="." method="_on_color_picker_popup_closed"]
diff --git a/flumi/Scripts/AutoSizingFlexContainer.gd b/flumi/Scripts/AutoSizingFlexContainer.gd
index 32f8e70..eb396af 100644
--- a/flumi/Scripts/AutoSizingFlexContainer.gd
+++ b/flumi/Scripts/AutoSizingFlexContainer.gd
@@ -88,7 +88,7 @@ func _resort() -> void:
if not auto_size_width:
available_width = calculate_available_dimension(true)
- elif flex_wrap == FlexContainer.FlexWrap.Wrap:
+ elif flex_wrap != FlexContainer.FlexWrap.NoWrap:
available_width = get_parent_or_fallback_size(true)
if not auto_size_height:
diff --git a/flumi/Scripts/B9/HTMLParser.gd b/flumi/Scripts/B9/HTMLParser.gd
index 5cbf6b5..58b9086 100644
--- a/flumi/Scripts/B9/HTMLParser.gd
+++ b/flumi/Scripts/B9/HTMLParser.gd
@@ -170,7 +170,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", "font-bold", "font-italic", "underline"]
+ var inheritable_properties = ["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)
diff --git a/flumi/Scripts/B9/Lua.gd b/flumi/Scripts/B9/Lua.gd
index 32859b9..185a6c2 100644
--- a/flumi/Scripts/B9/Lua.gd
+++ b/flumi/Scripts/B9/Lua.gd
@@ -663,10 +663,7 @@ func _handle_text_setting(operation: Dictionary):
var text_node = get_dom_node(dom_node, "text")
if text_node:
if text_node is RichTextLabel:
- var formatted_text = element.get_bbcode_formatted_text(dom_parser)
- formatted_text = "[font_size=24]%s[/font_size]" % formatted_text
-
- text_node.text = formatted_text
+ StyleManager.apply_styles_to_label(text_node, dom_parser.get_element_styles_with_inheritance(element, "", []), element, dom_parser, text)
text_node.call_deferred("_auto_resize_to_content")
elif text_node.has_method("set_text"):
text_node.set_text(text)
@@ -677,10 +674,7 @@ func _handle_text_setting(operation: Dictionary):
else:
var rich_text_label = _find_rich_text_label_recursive(dom_node)
if rich_text_label:
- var formatted_text = element.get_bbcode_formatted_text(dom_parser)
- formatted_text = "[font_size=24]%s[/font_size]" % formatted_text
-
- rich_text_label.text = formatted_text
+ StyleManager.apply_styles_to_label(rich_text_label, dom_parser.get_element_styles_with_inheritance(element, "", []), element, dom_parser, text)
rich_text_label.call_deferred("_auto_resize_to_content")
func _find_rich_text_label_recursive(node: Node) -> RichTextLabel:
diff --git a/flumi/Scripts/StyleManager.gd b/flumi/Scripts/StyleManager.gd
index 26553d5..37a547f 100644
--- a/flumi/Scripts/StyleManager.gd
+++ b/flumi/Scripts/StyleManager.gd
@@ -60,55 +60,24 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
if styles.has("height"):
height = parse_size(styles["height"])
- # Skip width/height inheritance for buttons when inheriting from auto-sized containers
var skip_sizing = SizingUtils.should_skip_sizing(node, element, parser)
if (width != null or height != null) and not skip_sizing:
- # FlexContainers handle percentage sizing differently than regular controls
- if node is FlexContainer:
- if width != null:
- if SizingUtils.is_percentage(width):
- # For FlexContainers with percentage width, use proportion sizing
- var percentage_value = float(width.replace("%", "")) / 100.0
- node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
- node.size_flags_stretch_ratio = percentage_value
- else:
- node.custom_minimum_size.x = width
- node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
- node.set_meta("size_flags_horizontal_set", true)
-
- if height != null:
- if SizingUtils.is_percentage(height):
- node.size_flags_vertical = Control.SIZE_EXPAND_FILL
- else:
- node.custom_minimum_size.y = height
- node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
- node.set_meta("size_flags_vertical_set", true)
-
- node.set_meta("size_flags_set_by_style_manager", true)
- elif node is VBoxContainer or node is HBoxContainer or node is Container:
- # Hcontainer nodes (like ul, ol)
- SizingUtils.apply_container_dimension_sizing(node, width, height, styles)
- elif node is HTMLP:
- # Only apply sizing if element has explicit size, otherwise preserve natural sizing
- var element_styles = parser.get_element_styles_internal(element, "")
- if element_styles.has("width") or element_styles.has("height"):
- var orig_h_flag = node.size_flags_horizontal
- var orig_v_flag = node.size_flags_vertical
- SizingUtils.apply_regular_control_sizing(node, width, height, styles)
- if not element_styles.has("width"):
- node.size_flags_horizontal = orig_h_flag
- if not element_styles.has("height"):
- node.size_flags_vertical = orig_v_flag
- else:
- if element.tag_name == "img" and SizingUtils.is_percentage(width) and SizingUtils.is_percentage(height):
+ if width != null:
+ if width is String and width == "100%":
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
- node.size_flags_vertical = Control.SIZE_EXPAND_FILL
- # Clear any hardcoded sizing
- node.custom_minimum_size = Vector2.ZERO
+ node.custom_minimum_size.x = 0
else:
- # regular controls
- SizingUtils.apply_regular_control_sizing(node, width, height, styles)
+ node.custom_minimum_size.x = width
+ node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
+
+ if height != null:
+ if height is String and height == "100%":
+ node.size_flags_vertical = Control.SIZE_EXPAND_FILL
+ node.custom_minimum_size.y = 0
+ else:
+ node.custom_minimum_size.y = height
+ node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
apply_element_centering(node, styles)
@@ -539,7 +508,7 @@ static func apply_body_styles(body: HTMLParser.HTMLElement, parser: HTMLParser,
original_parent.move_child(margin_container, container_index)
margin_container.add_child(website_container)
- var padding_val = parse_size(styles["padding"])
+ var padding_val = parse_size(styles["padding"] if styles.has("padding") else 0)
margin_container.add_theme_constant_override("margin_left", padding_val)
margin_container.add_theme_constant_override("margin_right", padding_val)
diff --git a/flumi/Scripts/Utils/FlexUtils.gd b/flumi/Scripts/Utils/FlexUtils.gd
index a5ea22f..cf99212 100644
--- a/flumi/Scripts/Utils/FlexUtils.gd
+++ b/flumi/Scripts/Utils/FlexUtils.gd
@@ -61,7 +61,7 @@ static func apply_flex_container_properties(node, styles: Dictionary) -> void:
if styles.has("width"):
var width_val = styles["width"]
- if width_val == "full":
+ if width_val == "full" or width_val == "100%":
# For flex containers, w-full should expand to fill parent
node.set_meta("should_fill_horizontal", true)
elif typeof(width_val) == TYPE_STRING and width_val.ends_with("%"):
@@ -79,6 +79,7 @@ static func apply_flex_container_properties(node, styles: Dictionary) -> void:
node.set_meta("custom_css_height", SizingUtils.parse_size_value(height_val))
if styles.has("background-color"):
node.set_meta("custom_css_background_color", styles["background-color"])
+
node.update_layout()
static func apply_flex_item_properties(node: Control, styles: Dictionary) -> void:
diff --git a/flumi/Scripts/Utils/Lua/DOM.gd b/flumi/Scripts/Utils/Lua/DOM.gd
index 9fb6f83..b1fc145 100644
--- a/flumi/Scripts/Utils/Lua/DOM.gd
+++ b/flumi/Scripts/Utils/Lua/DOM.gd
@@ -290,6 +290,84 @@ static func render_new_element(element: HTMLParser.HTMLElement, parent_node: Nod
main_scene.safe_add_child(container_node, element_node)
+static func _get_input_value(element: HTMLParser.HTMLElement, dom_node: Node) -> Variant:
+ var input_type = element.get_attribute("type").to_lower()
+
+ match input_type:
+ "checkbox", "radio":
+ if dom_node is CheckBox:
+ return dom_node.button_pressed
+ return false
+ "color":
+ if dom_node is ColorPickerButton:
+ return "#" + dom_node.color.to_html(false)
+ return "#ffffff"
+ "range":
+ if dom_node is HSlider:
+ return dom_node.value
+ return 0.0
+ "number":
+ if dom_node is SpinBox:
+ return dom_node.value
+ return 0.0
+ "file":
+ # For file inputs, need to find the input control that has get_file_info method
+ var input_control = _find_input_control_with_file_info(dom_node)
+ if input_control:
+ var file_info = input_control.get_file_info()
+ return file_info.get("fileName", "")
+ return ""
+ "date":
+ if dom_node is DateButton:
+ if dom_node.has_method("get_date_text"):
+ return dom_node.get_date_text()
+ return ""
+ _: # text, password, email, etc.
+ if dom_node is LineEdit:
+ return dom_node.text
+ elif dom_node is TextEdit:
+ return dom_node.text
+ return ""
+
+static func _find_input_control_with_file_info(node: Node) -> Node:
+ if node and node.has_method("get_file_info"):
+ return node
+
+ var current = node
+ while current:
+ current = current.get_parent()
+ if current and current.has_method("get_file_info"):
+ return current
+
+ return null
+
+static func _set_input_value(element: HTMLParser.HTMLElement, dom_node: Node, value: Variant) -> void:
+ var input_type = element.get_attribute("type").to_lower()
+
+ match input_type:
+ "checkbox", "radio":
+ if dom_node is CheckBox:
+ dom_node.button_pressed = bool(value)
+ "color":
+ if dom_node is ColorPickerButton:
+ var color_value = str(value)
+ if color_value.begins_with("#"):
+ dom_node.color = Color.from_string(color_value, Color.WHITE)
+ "range":
+ if dom_node is HSlider:
+ dom_node.value = float(value)
+ "number":
+ if dom_node is SpinBox:
+ dom_node.value = float(value)
+ "date":
+ if dom_node is DateButton and dom_node.has_method("set_date_from_string"):
+ dom_node.set_date_from_string(str(value))
+ _: # text, password, email, etc.
+ if dom_node is LineEdit:
+ dom_node.text = str(value)
+ elif dom_node is TextEdit:
+ dom_node.text = str(value)
+
# Helper functions
static func find_element_by_id(element_id: String, dom_parser: HTMLParser) -> HTMLParser.HTMLElement:
if element_id == "body":
@@ -852,6 +930,23 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
return LuaAudioUtils.handle_dom_audio_index(vm, element_id, key)
match key:
+ "value":
+ if lua_api and tag_name == "input":
+ vm.lua_getfield(1, "_element_id")
+ var element_id: String = vm.lua_tostring(-1)
+ vm.lua_pop(1)
+
+ var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
+ var dom_node = lua_api.dom_parser.parse_result.dom_nodes.get(element_id, null)
+
+ if element and dom_node:
+ var input_value = _get_input_value(element, dom_node)
+ vm.lua_pushstring(str(input_value))
+ return 1
+
+ # Fallback to empty string
+ vm.lua_pushstring("")
+ return 1
"text":
if lua_api:
# Get element ID and find the element
@@ -929,12 +1024,10 @@ static func _element_index_wrapper(vm: LuauVM) -> int:
return 1
static func _add_classlist_support(vm: LuauVM, lua_api: LuaAPI) -> void:
- # Create classList table with threaded methods
vm.lua_newtable()
- # Store the element_id in the classList table
- vm.lua_getfield(-2, "_element_id") # Get element_id from parent element
- vm.lua_setfield(-2, "_element_id") # Store it in classList table
+ vm.lua_getfield(-2, "_element_id")
+ vm.lua_setfield(-2, "_element_id")
# Add classList methods
vm.lua_pushcallable(LuaDOMUtils._classlist_add_wrapper, "classList.add")
@@ -961,12 +1054,11 @@ static func _add_classlist_support(vm: LuauVM, lua_api: LuaAPI) -> void:
vm.lua_setfield(-2, "classList")
static func _classlist_add_wrapper(vm: LuauVM) -> int:
- # Get lua_api from VM metadata
var lua_api = vm.get_meta("lua_api") as LuaAPI
if not lua_api:
return 0
- vm.luaL_checktype(1, vm.LUA_TTABLE) # classList table
+ vm.luaL_checktype(1, vm.LUA_TTABLE)
var cls: String = vm.luaL_checkstring(2)
# Get element_id from classList table
@@ -1132,6 +1224,21 @@ static func _element_newindex_wrapper(vm: LuauVM) -> int:
return LuaAudioUtils.handle_dom_audio_newindex(vm, element_id, key, value)
match key:
+ "value":
+ if tag_name == "input":
+ vm.lua_getfield(1, "_element_id")
+ var element_id: String = vm.lua_tostring(-1)
+ vm.lua_pop(1)
+
+ var element = lua_api.dom_parser.find_by_id(element_id) if element_id != "body" else lua_api.dom_parser.find_first("body")
+ var dom_node = lua_api.dom_parser.parse_result.dom_nodes.get(element_id, null)
+
+ if element and dom_node:
+ # Update the HTML element's value attribute
+ element.set_attribute("value", str(value))
+
+ _set_input_value(element, dom_node, value)
+ return 0
"text":
var text: String = str(value) # Convert value to string
diff --git a/flumi/Scripts/main.gd b/flumi/Scripts/main.gd
index da4fae5..e39da58 100644
--- a/flumi/Scripts/main.gd
+++ b/flumi/Scripts/main.gd
@@ -53,18 +53,6 @@ func _ready():
ProjectSettings.set_setting("display/window/size/min_width", MIN_SIZE.x)
ProjectSettings.set_setting("display/window/size/min_height", MIN_SIZE.y)
DisplayServer.window_set_min_size(MIN_SIZE)
-
- get_viewport().size_changed.connect(_on_viewport_size_changed)
-
-func _on_viewport_size_changed():
- recalculate_percentage_elements(website_container)
-
-func recalculate_percentage_elements(node: Node):
- if node is Control and node.has_meta("needs_percentage_recalc"):
- SizingUtils.apply_container_percentage_sizing(node)
-
- for child in node.get_children():
- recalculate_percentage_elements(child)
var current_domain = "" # Store current domain for display
@@ -267,9 +255,6 @@ static func safe_add_child(parent: Node, child: Node) -> void:
child.get_parent().remove_child(child)
parent.add_child(child)
- if child.has_meta("container_percentage_width") or child.has_meta("container_percentage_height"):
- SizingUtils.apply_container_percentage_sizing(child)
- child.set_meta("needs_percentage_recalc", true)
func contains_hyperlink(element: HTMLParser.HTMLElement) -> bool:
if element.tag_name == "a":
@@ -290,6 +275,7 @@ func is_text_only_element(element: HTMLParser.HTMLElement) -> bool:
func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) -> Control:
var styles = parser.get_element_styles_with_inheritance(element, "", [])
+ var hover_styles = parser.get_element_styles_with_inheritance(element, "hover", [])
var is_flex_container = styles.has("display") and ("flex" in styles["display"])
var final_node: Control
@@ -308,10 +294,22 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
if is_flex_container:
# The element's primary identity IS a flex container.
- # We create it directly.
- final_node = AUTO_SIZING_FLEX_CONTAINER.new()
- final_node.name = "Flex_" + element.tag_name
- container_for_children = final_node
+ if element.tag_name == "div":
+ if BackgroundUtils.needs_background_wrapper(styles) or hover_styles.size() > 0:
+ final_node = BackgroundUtils.create_panel_container_with_background(styles, hover_styles)
+ var flex_container = AUTO_SIZING_FLEX_CONTAINER.new()
+ flex_container.name = "Flex_" + element.tag_name
+ var vbox = final_node.get_child(0) as VBoxContainer
+ vbox.add_child(flex_container)
+ container_for_children = flex_container
+ else:
+ final_node = AUTO_SIZING_FLEX_CONTAINER.new()
+ final_node.name = "Flex_" + element.tag_name
+ container_for_children = final_node
+ else:
+ final_node = AUTO_SIZING_FLEX_CONTAINER.new()
+ final_node.name = "Flex_" + element.tag_name
+ container_for_children = final_node
# For FLEX ul/ol elements, we need to create the li children directly in the flex container
if element.tag_name == "ul" or element.tag_name == "ol":
@@ -353,11 +351,20 @@ func create_element_node(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
# Apply flex CONTAINER properties if it's a flex container
if is_flex_container:
var flex_container_node = final_node
- # If the node was wrapped in a MarginContainer, get the inner FlexContainer
- if final_node is MarginContainer and final_node.get_child_count() > 0:
+
+ if final_node is FlexContainer:
+ # Direct FlexContainer
+ flex_container_node = final_node
+ elif final_node is MarginContainer and final_node.get_child_count() > 0:
var first_child = final_node.get_child(0)
if first_child is FlexContainer:
flex_container_node = first_child
+ elif final_node is PanelContainer and final_node.get_child_count() > 0:
+ var vbox = final_node.get_child(0)
+ if vbox is VBoxContainer and vbox.get_child_count() > 0:
+ var potential_flex = vbox.get_child(0)
+ if potential_flex is FlexContainer:
+ flex_container_node = potential_flex
if flex_container_node is FlexContainer:
FlexUtils.apply_flex_container_properties(flex_container_node, styles)
@@ -476,7 +483,7 @@ func create_element_node_internal(element: HTMLParser.HTMLElement, parser: HTMLP
var hover_styles = parser.get_element_styles_with_inheritance(element, "hover", [])
var is_flex_container = styles.has("display") and ("flex" in styles["display"])
- # For flex divs, don't create div scene - the AutoSizingFlexContainer handles it
+ # For flex divs, let the general flex container logic handle them
if is_flex_container:
return null