Initial commit
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s

Assisted-by: GLM 5.3
This commit is contained in:
2026-09-29 10:03:32 +02:00
commit f8ed33df83
206 changed files with 44165 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
{{define "title"}}{{ tr "Set up Volumen" }} · {{ tr "Volumen admin" }}{{end}}
{{define "login_content"}}
<div class="login__card setup">
<h1 class="login__title" data-i18n="Welcome to Volumen">{{ tr "Welcome to Volumen" }}</h1>
<p class="login__pitch" data-i18n="Set up the administrator account to open this installation.">{{ tr "Set up the administrator account to open this installation." }}</p>
{{if .Error}}
<div class="status status--error" role="alert">
<svg aria-hidden="true"><use href="/admin/assets/graphis.svg#alert-circle"></use></svg>
<div>{{ .Error }}</div>
</div>
{{end}}
<form method="post" action="/admin/setup" class="stack">
<input type="hidden" name="_csrf" value="{{ .CSRFToken }}">
<input type="hidden" name="language" id="setup-language" value="{{ .Lang }}">
<fieldset class="setup__group">
<legend data-i18n="Account">{{ tr "Account" }}</legend>
<div class="grid-2">
<div class="field">
<label class="field__label" for="username" data-i18n="Username">{{ tr "Username" }}</label>
<input id="username" class="control mono" type="text" name="username" value="admin"
autocomplete="username" required autofocus>
</div>
<div class="field">
<label class="field__label" for="name" data-i18n="Display name">{{ tr "Display name" }}</label>
<input id="name" class="control" type="text" name="name" autocomplete="name"
placeholder="{{ tr "Your real name" }}" data-i18n-placeholder="Your real name">
</div>
</div>
<div class="field">
<label class="field__label" for="password" data-i18n="Password">{{ tr "Password" }}</label>
<div class="pw">
<input id="password" class="control" type="password" name="password"
autocomplete="new-password" required data-pw-meter
data-min-length="{{ .Config.Admin.MinPasswordLength }}"
aria-describedby="pw-strength-text">
<button type="button" class="pw__toggle" id="pw-toggle" aria-label="{{ tr "Show password" }}">
<svg aria-hidden="true"><use href="/admin/assets/graphis.svg#eye"></use></svg>
</button>
</div>
<div class="pw-level" id="pw-level" data-level="0">
<div class="pw-level__bar" aria-hidden="true"><i></i><i></i><i></i><i></i></div>
<span class="pw-level__label" id="pw-strength-text" role="status" aria-live="polite"></span>
</div>
<p class="field__hint" data-i18n="password.hint">{{ trn .Config.Admin.MinPasswordLength "password.min" }}</p>
</div>
</fieldset>
<fieldset class="setup__group">
<legend data-i18n="Language">{{ tr "Language" }}</legend>
<div class="pill-group">
<a class="chip chip--switch" lang="en" href="/admin/setup?lang=en" data-lang-chip="en"
{{if eq .Lang "en"}}aria-current="true"{{end}}>English</a>
<a class="chip chip--switch" lang="cs" href="/admin/setup?lang=cs" data-lang-chip="cs"
{{if eq .Lang "cs"}}aria-current="true"{{end}}>Čeština</a>
</div>
</fieldset>
<fieldset class="setup__group">
<legend data-i18n="Colour scheme">{{ tr "Colour scheme" }}</legend>
<div class="pill-group pill-group--themes">
<label class="chip chip--radio chip--theme">
<input type="radio" name="theme" value="viridis" {{if eq .Theme "viridis"}}checked{{end}}>
<span class="chip__swatch chip__swatch--viridis" aria-hidden="true"></span><span>Viridis</span>
</label>
<label class="chip chip--radio chip--theme">
<input type="radio" name="theme" value="plasma" {{if eq .Theme "plasma"}}checked{{end}}>
<span class="chip__swatch chip__swatch--plasma" aria-hidden="true"></span><span>Plasma</span>
</label>
<label class="chip chip--radio chip--theme">
<input type="radio" name="theme" value="magma" {{if eq .Theme "magma"}}checked{{end}}>
<span class="chip__swatch chip__swatch--magma" aria-hidden="true"></span><span>Magma</span>
</label>
</div>
<p class="field__hint" data-i18n="The page takes the colours as you choose.">{{ tr "The page takes the colours as you choose." }}</p>
</fieldset>
<button type="submit" class="btn btn--primary login__submit">
<span data-i18n="Create account">{{ tr "Create account" }}</span>
<svg aria-hidden="true"><use href="/admin/assets/graphis.svg#arrow-right"></use></svg>
</button>
</form>
</div>
<script nonce="{{ .CSPNonce }}">
(() => {
const btn = document.getElementById("pw-toggle");
const input = document.getElementById("password");
btn.addEventListener("click", () => {
const show = input.type === "password";
input.type = show ? "text" : "password";
btn.setAttribute("aria-label", window.__(show ? "Hide password" : "Show password"));
});
/* The wizard previews its own choices: the palette rides the same
html attribute the sheets read, and the language swaps the page
text from the bundle the server carried. The chips are real
links to the same page with ?lang=, so both choices survive a
browser without JavaScript: the link reloads, the theme radios
submit. */
const BUNDLE = {{ .SetupI18n }};
const apply = (lang) => {
document.documentElement.lang = lang;
document.querySelectorAll("[data-i18n]").forEach((el) => {
const pair = BUNDLE.ui[el.dataset.i18n];
if (pair && pair[lang]) el.textContent = pair[lang];
});
document.querySelectorAll("[data-i18n-placeholder]").forEach((el) => {
const pair = BUNDLE.ui[el.dataset.i18nPlaceholder];
if (pair && pair[lang]) el.placeholder = pair[lang];
});
window.__volumen.i18n = BUNDLE.js[lang];
btn.setAttribute("aria-label",
window.__(input.type === "password" ? "Show password" : "Hide password"));
/* The meter's label comes from the shared catalogue: re-score
the field so the word follows the language swap. */
input.dispatchEvent(new Event("input"));
document.getElementById("setup-language").value = lang;
document.querySelectorAll("[data-lang-chip]").forEach((chip) => {
if (chip.dataset.langChip === lang) chip.setAttribute("aria-current", "true");
else chip.removeAttribute("aria-current");
});
};
document.querySelectorAll("[data-lang-chip]").forEach((chip) => {
chip.addEventListener("click", (event) => {
event.preventDefault();
apply(chip.dataset.langChip);
});
});
document.querySelectorAll('input[name="theme"]').forEach((radio) => {
radio.addEventListener("change", () => {
document.documentElement.dataset.palette = radio.value;
});
});
/* The strength meter itself is the shared one the layout wires
for every input[data-pw-meter]; only the label refresh on a
language switch is local to this page. */
})();
</script>
{{end}}