{# The sign-in panel — the part Swagger UI cannot have. Everything on it comes from `AuthFlow.state()`, which is the app's own code: the headline, the fields, the facts worth showing. This template knows none of it, which is the point — a flow signing in with one API key and a flow running an OIDC password grant render through the same file. A partial, so it renders standalone: the page embeds it, and the POST that signs in or out swaps it in place. Nothing else on the page moves. #} {% from "apidocs/macros.html" import facts %} {% from "ui/button.html" import button %} {% from "ui/data.html" import badge, card %} {% from "ui/feedback.html" import spinner %} {% from "ui/form.html" import form, text_field %} {% from "ui/icon.html" import icon %}
{{ flow_state.detail }}
{% endif %} {% if flow_state.error %} {# Beside the fields rather than replacing them: re-rendering the panel lost whatever was typed, and the form still has to be there to try again. `role="alert"` so a screen reader hears the refusal — the swap is otherwise silent, and the visible change is off-screen. #}{{ icon("circle-alert", 16) }}{{ flow_state.error }}
{% endif %} {% if flow_state.facts %}{{ facts(flow_state.facts) }}{% endif %} {% if not flow_state.signed_in and flow_state.fields %} {% call form(action=url_for(request, routes.auth), target="#" ~ ids.session, card=false) %} {# Fields and the submit button on one line — a wrapping flex row, deliberately not `grid` or `field_row`. A username and a password are short values: a fixed `sm:w-64` is about as wide as either is ever typed, and one per half card width reads as though something long is expected. The row wraps rather than reflowing to a column count, so it is right for one field and for four — which matters, because the flow decides how many there are, not this template. `items-end` aligns the bottoms, which puts the button on the inputs' line. That alignment is why the fields carry no `hint=`: one field with a hint and one without have different heights, and bottom-aligning those two would leave their inputs on different lines. The hints are collected under the row instead — one line however many fields carry one, in the order they were declared. Each stays wired to its own input: `aria-describedby` names the id `text_field` would itself have given the hint, so a screen reader still hears "the demo account" on the field it belongs to rather than as loose text after the form. #} {%- set hints = flow_state.fields | selectattr("hint") | list %}{%- for entry in hints -%} {% if not loop.first %} · {% endif %} {{ entry.label }}: {{ entry.hint }} {%- endfor -%}
{% endif %}