add: small littleweb browser changes

This commit is contained in:
trisua 2025-07-08 17:38:24 -04:00
parent e7febc7c7e
commit 388ccbf58c
9 changed files with 194 additions and 21 deletions

View file

@ -32,7 +32,7 @@
(input
("type" "uri")
("class" "w-full")
("true_value" "{{ path }}")
("true_value" "")
("name" "uri")
("id" "uri"))
@ -121,10 +121,6 @@
uri = `${uri}/index.html`;
}
if (!uri.startsWith(\"atto://\")) {
uri = `atto://${uri}`;
}
// ...
console.log(\"navigate\", uri);
document.getElementById(\"browser_iframe\").src = `{{ config.lw_host|safe }}/api/v1/net/${uri}`;
@ -152,7 +148,8 @@
if (data.event === \"change_url\") {
const uri = new URL(data.target).pathname.slice(\"/api/v1/net/\".length);
window.history.pushState(null, null, `/net/${uri.replace(\"atto://\", \"\")}`);
document.getElementById(\"uri\").setAttribute(\"true_value\", uri);
document.getElementById(\"uri\").setAttribute(\"true_value\", `atto://${uri}`);
document.getElementById(\"uri\").value = uri.split(\"/\")[0];
}
});
@ -207,6 +204,7 @@
is_focused = false;
});
document.getElementById(\"uri\").value = document.getElementById(\"uri\").getAttribute(\"true_value\").replace(\"atto://\", \"\").split(\"/\")[0]"))
// navigate
littleweb_navigate(\"{{ path|safe }}\");"))
(text "{% endblock %}")

View file

@ -5,6 +5,17 @@
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
(main
("class" "flex flex-col gap-2")
; viewing other user's domains warning
(text "{% if profile.id != user.id -%}")
(div
("class" "card w-full red flex gap-2 items-center")
(text "{{ icon \"skull\" }}")
(b
(text "Viewing other user's domains! Please be careful.")))
(text "{%- endif %}")
; ...
(text "{% if user -%}")
(div
("class" "pillmenu")

View file

@ -5,6 +5,17 @@
(text "{% endblock %} {% block body %} {{ macros::nav() }}")
(main
("class" "flex flex-col gap-2")
; viewing other user's services warning
(text "{% if profile.id != user.id -%}")
(div
("class" "card w-full red flex gap-2 items-center")
(text "{{ icon \"skull\" }}")
(b
(text "Viewing other user's sites! Please be careful.")))
(text "{%- endif %}")
; ...
(text "{% if user -%}")
(div
("class" "pillmenu")

View file

@ -50,6 +50,18 @@
(span
("class" "notification")
(text "{{ profile.request_count }}")))
(a
("href" "/services?id={{ profile.id }}")
("class" "button lowered")
(icon (text "globe"))
(span
(text "Sites")))
(a
("href" "/domains?id={{ profile.id }}")
("class" "button lowered")
(icon (text "globe"))
(span
(text "Domains")))
(button
("class" "red lowered")
("onclick" "delete_account(event)")
@ -155,6 +167,33 @@
});
};
globalThis.update_user_secondary_role = async (new_role) => {
if (
!(await trigger(\"atto::confirm\", [
\"Are you sure you would like to do this?\",
]))
) {
return;
}
fetch(`/api/v1/auth/user/{{ profile.id }}/role/2`, {
method: \"POST\",
headers: {
\"Content-Type\": \"application/json\",
},
body: JSON.stringify({
role: Number.parseInt(new_role),
}),
})
.then((res) => res.json())
.then((res) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
});
};
ui.refresh_container(element, [\"actions\"]);
setTimeout(() => {
@ -178,6 +217,11 @@
\"{{ profile.permissions }}\",
\"input\",
],
[
[\"secondary_role\", \"Secondary permission level\"],
\"{{ profile.secondary_permissions }}\",
\"input\",
],
],
null,
{
@ -194,6 +238,9 @@
role: (new_role) => {
return update_user_role(new_role);
},
secondary_role: (new_role) => {
return update_user_secondary_role(new_role);
},
},
);
}, 100);
@ -244,6 +291,24 @@
(div
("class" "card lowered flex flex-col gap-2")
("id" "permission_builder")))
(div
("class" "card-nest w-full")
(div
("class" "card small flex items-center justify-between gap-2")
(div
("class" "flex items-center gap-2")
(text "{{ icon \"blocks\" }}")
(span
(text "{{ text \"mod_panel:label.permissions_level_builder\" }}")))
(button
("class" "small lowered")
("onclick" "update_user_secondary_role(Number.parseInt(document.getElementById('role').value))")
(text "{{ icon \"check\" }}")
(span
(text "{{ text \"general:action.save\" }}"))))
(div
("class" "card lowered flex flex-col gap-2")
("id" "secondary_permission_builder")))
(script
(text "setTimeout(async () => {
const get_permissions_html = await trigger(
@ -291,6 +356,30 @@
Number.parseInt(\"{{ profile.permissions }}\"),
\"permission_builder\",
);
}, 250);
setTimeout(async () => {
const get_permissions_html = await trigger(
\"ui::generate_permissions_ui\",
[
{
// https://trisuaso.github.io/tetratto/tetratto/model/permissions/struct.SecondaryPermission.html
DEFAULT: 1 << 0,
ADMINISTRATOR: 1 << 1,
MANAGE_DOMAINS: 1 << 2,
MANAGE_SECONDARY: 1 << 3,
},
\"secondary_role\",
\"add_permission_to_secondary_role\",
\"remove_permission_to_secondary_role\",
],
);
document.getElementById(\"secondary_permission_builder\").innerHTML =
get_permissions_html(
Number.parseInt(\"{{ profile.secondary_permissions }}\"),
\"secondary_permission_builder\",
);
}, 250);")))
(text "{% endblock %}")

View file

@ -1069,7 +1069,13 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
// permissions ui
self.define(
"generate_permissions_ui",
(_, permissions, field_id = "role") => {
(
_,
permissions,
field_id = "role",
add_name = "add_permission_to_role",
remove_name = "remove_permission_from_role",
) => {
function all_matching_permissions(role) {
const matching = [];
const not_matching = [];
@ -1099,7 +1105,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
function get_permissions_html(role, id) {
const [matching, not_matching] = all_matching_permissions(role);
globalThis.remove_permission_from_role = (permission) => {
globalThis[remove_name] = (permission) => {
matching.splice(matching.indexOf(permission), 1);
not_matching.push(permission);
@ -1107,7 +1113,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
get_permissions_html(rebuild_role(matching), id);
};
globalThis.add_permission_to_role = (permission) => {
globalThis[add_name] = (permission) => {
not_matching.splice(not_matching.indexOf(permission), 1);
matching.push(permission);
@ -1120,14 +1126,14 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
for (const match of matching) {
permissions_html += `<div class="card w-full secondary flex justify-between gap-2">
<span>${match} <code>${permissions[match]}</code></span>
<button class="red lowered" onclick="remove_permission_from_role('${match}')">Remove</button>
<button class="red lowered" onclick="${remove_name}('${match}')">Remove</button>
</div>`;
}
for (const match of not_matching) {
permissions_html += `<div class="card w-full secondary flex justify-between gap-2">
<span>${match} <code>${permissions[match]}</code></span>
<button class="green lowered" onclick="add_permission_to_role('${match}')">Add</button>
<button class="green lowered" onclick="${add_name}('${match}')">Add</button>
</div>`;
}

View file

@ -54,7 +54,7 @@ function fix_atto_links() {
`atto://${path.replace("atto://", "").split("/")[0]}${x}`;
} else {
y[property] =
`/api/v1/net/atto://${path.replace("atto://", "").split("/")[0]}${x}`;
`/api/v1/net/${path.replace("atto://", "").split("/")[0]}${x}`;
}
}
}