{# The panel shell. Three things are present on every page: the machine strip, so you always know which box you are about to change; the navigation, with live counts; and the log drawer, docked at the bottom rather than hidden behind a click per resource. #} {% macro nav_item(href, label, key, count) -%} {{ label }} {%- if count is not none %}{{ count }}{% endif %} {%- endmacro %} {% block title %}{{ machine.hostname }}{% endblock %} - WASM {# Everything is served from this host. The panel works on a machine with no route to the internet, which is rather the point of self-hosting. #} {# Every mutation htmx sends carries the session's CSRF token. The header name comes from the server rather than being typed here: the shell used to send "X-CSRF-Token" while wasm.web.auth reads CSRF_HEADER_NAME, which is "X-WASM-CSRF", so every restart, every delete and the sign-out button came back 403 from a browser. Two spellings of one name in two files cannot be kept in agreement by care alone. #} Skip to content {# The signature element: one dense instrument line for this machine. The fragment carries its own element so htmx can swap it whole. #} {% include "fragments/machine.html" %}
{% block content %}{% endblock %}
{# The docked log drawer. It costs vertical space, and that is the bet: the moment logs matter most is mid-deploy, and hiding them behind a click per resource is what makes a web panel feel worse than an SSH session. #}
{# The bar is a plain element that happens to be clickable, and the toggle is a real button. It used to be the other way round: the bar carried role="button" and wrapped two more buttons, which is invalid nesting, and its keyboard handling was hand-rolled. A native button is focusable, operable with Enter and Space, and announced correctly without any of that. #}
Log {# The fallback text matters: x-text replaces the content, so before Alpine runs an empty element would leave the bar looking broken. #} nothing attached