Accessibility pass

This commit is contained in:
Jonathon Leight committed 2026-06-20 20:54:24 -04:00
1 parent fea974f53e
commit b10e6e00ce
19 files changed
+114 -93

No files matched your search

+16 -3
View File
@@ -34,7 +34,9 @@ code.pubkey {
border-radius: var(--tblr-border-radius);
padding: 0.5rem 0.625rem;
font-size: 0.8125rem;
color: var(--tblr-secondary);
/* gray-400, not --tblr-secondary (#6b7280): the latter is only 3.67:1 on the
near-black panel bg and fails WCAG AA for this key/data text. */
color: var(--tblr-gray-400);
}
/* ---------- Live event / console log ---------- */
@@ -54,14 +56,25 @@ code.pubkey {
.evlog.console { max-height: 320px; }
.ev { padding: 0.15rem 0; border-bottom: 1px solid var(--tblr-border-color-translucent, rgba(255, 255, 255, 0.05)); }
.ev:last-child { border-bottom: none; }
.ev-info { color: var(--tblr-secondary); }
/* Colors chosen for WCAG AA (≥4.5:1) on the near-black log background:
--tblr-secondary (#6b7280) and --tblr-danger (#d63939) only reach ~3.7:1,
so info uses the lighter gray-400 and errors the lighter danger emphasis. */
.ev-info { color: var(--tblr-gray-400); }
.ev-confirmed { color: var(--tblr-success); font-weight: 600; }
.ev-error, .ev-timeout { color: var(--tblr-danger); }
.ev-error, .ev-timeout { color: var(--tblr-danger-text-emphasis); }
.ev-warning, .ev-denied, .ev-noreply { color: var(--tblr-warning); }
.ev-debug { color: var(--tblr-info); word-break: break-all; }
.ev-sent { color: var(--tblr-azure); }
.ev-reply { color: var(--tblr-body-color); white-space: pre-wrap; }
/* ---------- Badge contrast (dark) ----------
Tabler's light "-lt" badges set their text to the full-strength brand color
over a 10%-opacity tint. On the dark theme purple (#ae3ec9 → 2.80:1) and
secondary (#6b7280 → 2.74:1) fall below WCAG AA for this small status text;
lighten just those two. success/azure/warning/yellow already pass. */
.badge.bg-purple-lt { color: #d98fe6 !important; }
.badge.bg-secondary-lt { color: var(--tblr-secondary-text-emphasis) !important; }
/* ---------- Leaflet map (dark) ---------- */
#map {
height: 360px;
+8 -1
View File
@@ -207,5 +207,12 @@ document.addEventListener("click", function (e) {
if (!t) return;
e.preventDefault();
const input = document.getElementById(t.getAttribute("data-pwtoggle"));
if (input) input.type = input.type === "password" ? "text" : "password";
if (!input) return;
const showing = input.type !== "password";
input.type = showing ? "password" : "text";
// Keep the control's state in sync for screen readers: pressed = revealed.
const nowShowing = !showing;
t.setAttribute("aria-pressed", String(nowShowing));
t.setAttribute("aria-label", nowShowing ? "Hide password" : "Show password");
t.setAttribute("title", nowShowing ? "Hide password" : "Show password");
});
+8 -8
View File
@@ -19,13 +19,13 @@
<div class="card-body">
<form method="post" action="/account/profile">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" class="form-control" value="{{.User.Username}}" disabled>
<label class="form-label" for="acct_username">Username</label>
<input type="text" class="form-control" id="acct_username" value="{{.User.Username}}" disabled>
<small class="form-hint">Your username can't be changed.</small>
</div>
<div class="mb-3">
<label class="form-label">Display name <span class="form-label-description">optional</span></label>
<input type="text" class="form-control" name="display_name" maxlength="64" value="{{if .DisplayName}}{{.DisplayName}}{{end}}" placeholder="Shown to people you share with">
<label class="form-label" for="acct_display_name">Display name <span class="form-label-description">optional</span></label>
<input type="text" class="form-control" id="acct_display_name" name="display_name" maxlength="64" value="{{if .DisplayName}}{{.DisplayName}}{{end}}" placeholder="Shown to people you share with">
</div>
<button type="submit" class="btn btn-primary">Save profile</button>
</form>
@@ -81,18 +81,18 @@
<form method="post" action="/account/password">
{{if .HasPassword}}
<div class="mb-3">
<label class="form-label">Current password</label>
<label class="form-label" for="cur_pw">Current password</label>
<div class="input-group input-group-flat">
<input type="password" class="form-control" id="cur_pw" name="current_password" autocomplete="current-password" required>
<span class="input-group-text"><a href="#" class="link-secondary" title="Show password" data-pwtoggle="cur_pw" aria-label="Show password">{{template "icon-eye" ""}}</a></span>
<span class="input-group-text"><a href="#" class="link-secondary" role="button" aria-pressed="false" title="Show password" data-pwtoggle="cur_pw" aria-label="Show password">{{template "icon-eye" ""}}</a></span>
</div>
</div>
{{end}}
<div class="mb-3">
<label class="form-label">{{if .HasPassword}}New password{{else}}Password{{end}} <span class="form-label-description">min 8 characters</span></label>
<label class="form-label" for="new_pw">{{if .HasPassword}}New password{{else}}Password{{end}} <span class="form-label-description">min 8 characters</span></label>
<div class="input-group input-group-flat">
<input type="password" class="form-control" id="new_pw" name="new_password" autocomplete="new-password" minlength="8" required>
<span class="input-group-text"><a href="#" class="link-secondary" title="Show password" data-pwtoggle="new_pw" aria-label="Show password">{{template "icon-eye" ""}}</a></span>
<span class="input-group-text"><a href="#" class="link-secondary" role="button" aria-pressed="false" title="Show password" data-pwtoggle="new_pw" aria-label="Show password">{{template "icon-eye" ""}}</a></span>
</div>
</div>
<button type="submit" class="btn btn-primary">{{if .HasPassword}}Change password{{else}}Set password{{end}}</button>
+14 -14
View File
@@ -42,11 +42,11 @@
</div>
<div class="mb-3">
<label class="form-label">MeshTender server public key</label>
<div class="form-label">MeshTender server public key</div>
<code class="pubkey">{{.ServerPubKey}}</code>
</div>
<div class="mb-3">
<label class="form-label">Grant-access command — run this on your repeater</label>
<div class="form-label">Grant-access command — run this on your repeater</div>
<div class="input-group">
<code class="form-control font-monospace">{{.SetPermCommand}}</code>
<button type="button" class="btn" onclick="navigator.clipboard.writeText(this.previousElementSibling.textContent)">{{template "icon-copy" ""}}Copy</button>
@@ -56,12 +56,12 @@
{{template "revokedoc" .RevokeCommand}}
<label class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="ack" onchange="var c=document.getElementById('continue');c.classList.toggle('disabled', !this.checked);c.toggleAttribute('disabled', !this.checked);">
<input class="form-check-input" type="checkbox" id="ack" aria-controls="continue" onchange="var c=document.getElementById('continue');var off=!this.checked;c.classList.toggle('disabled',off);c.setAttribute('aria-disabled',off);if(off){c.setAttribute('tabindex','-1');}else{c.removeAttribute('tabindex');}">
<span class="form-check-label">I've granted MeshTender admin on this repeater (or I understand I must, before it will work here).</span>
</label>
<div class="btn-list mt-3">
<a class="btn btn-primary disabled" id="continue" href="/repeaters/add?step=details" disabled
onclick="if(this.hasAttribute('disabled')){return false;}">Continue →</a>
<a class="btn btn-primary disabled" id="continue" href="/repeaters/add?step=details" aria-disabled="true" tabindex="-1"
onclick="if(this.getAttribute('aria-disabled')==='true'){return false;}">Continue →</a>
<a class="btn" href="/">Cancel</a>
</div>
</div>
@@ -74,15 +74,15 @@
<p class="text-secondary">Tell MeshTender which repeater you just granted access to, and how its radio is configured.</p>
<form method="post" action="/repeaters">
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="name" placeholder="Hilltop repeater" required autofocus>
<label class="form-label" for="name">Name</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Hilltop repeater" required autofocus>
</div>
<div class="mb-3">
<label class="form-label">Repeater public key (64 hex chars)</label>
<input type="text" class="form-control font-monospace" name="public_key" pattern="[0-9a-fA-F]{64}" placeholder="a1b2…" required>
<label class="form-label" for="public_key">Repeater public key (64 hex chars)</label>
<input type="text" class="form-control font-monospace" id="public_key" name="public_key" pattern="[0-9a-fA-F]{64}" placeholder="a1b2…" required>
</div>
<div class="mb-3">
<label class="form-label">Region preset</label>
<label class="form-label" for="region">Region preset</label>
<select class="form-select" id="region" onchange="applyRegion()">
{{range .Presets}}
<option value="{{.ID}}" data-freq="{{.FreqHz}}" data-bw="{{.BwHz}}" data-sf="{{.SF}}" data-cr="{{.CR}}"{{if eq .ID $.DefaultPresetID}} selected{{end}}>{{.Label}}</option>
@@ -91,10 +91,10 @@
</select>
</div>
<div class="row g-2">
<div class="col-6 col-md-3"><label class="form-label">Freq (Hz)</label><input type="number" class="form-control" id="radio_freq_hz" name="radio_freq_hz" value="{{.Defaults.FreqHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label">Bandwidth (Hz)</label><input type="number" class="form-control" id="radio_bw_hz" name="radio_bw_hz" value="{{.Defaults.BwHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label">SF</label><input type="number" class="form-control" id="radio_sf" name="radio_sf" value="{{.Defaults.SF}}" min="5" max="12" required></div>
<div class="col-6 col-md-3"><label class="form-label">CR</label><input type="number" class="form-control" id="radio_cr" name="radio_cr" value="{{.Defaults.CR}}" min="5" max="8" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_freq_hz">Freq (Hz)</label><input type="number" class="form-control" id="radio_freq_hz" name="radio_freq_hz" value="{{.Defaults.FreqHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_bw_hz">Bandwidth (Hz)</label><input type="number" class="form-control" id="radio_bw_hz" name="radio_bw_hz" value="{{.Defaults.BwHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_sf">SF</label><input type="number" class="form-control" id="radio_sf" name="radio_sf" value="{{.Defaults.SF}}" min="5" max="12" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_cr">CR</label><input type="number" class="form-control" id="radio_cr" name="radio_cr" value="{{.Defaults.CR}}" min="5" max="8" required></div>
</div>
<label class="form-check mt-3">
+2 -1
View File
@@ -10,6 +10,7 @@
<script src="/static/htmx.min.js" defer></script>
</head>
<body>
<a href="#main-content" class="visually-hidden-focusable btn btn-primary position-absolute top-0 start-0 m-2" style="z-index:1080">Skip to main content</a>
<div class="page">
<header class="navbar navbar-expand-md navbar-overlap d-print-none" data-bs-theme="dark">
<div class="container-xl">
@@ -85,7 +86,7 @@
</div>
</div>
<div class="page-body">
<div class="container-xl">
<div class="container-xl" id="main-content" tabindex="-1">
{{block "content" .}}{{end}}
</div>
</div>
+2 -2
View File
@@ -18,13 +18,13 @@
<code class="pk">{{.Repeater.PublicKeyHex}}</code>
<div class="text-secondary small mt-2">{{.Repeater.RadioFreqHz}} Hz · BW {{.Repeater.RadioBwHz}} · SF{{.Repeater.RadioSF}} · CR{{.Repeater.RadioCR}}</div>
<div id="unsupported" class="alert alert-danger mt-3" hidden>
<div id="unsupported" class="alert alert-danger mt-3" role="alert" hidden>
This browser does not support WebSerial. Use Chrome, Edge, or another Chromium-based browser over HTTPS or on localhost.
</div>
<button id="connect" class="btn btn-primary mt-3" type="button">{{template "icon-plug" "me-1"}}Connect modem &amp; confirm</button>
<ul id="log" class="evlog mt-3"></ul>
<ul id="log" class="evlog mt-3" role="log" aria-live="polite" aria-label="Confirmation activity"></ul>
<div class="mt-3">
<a class="back-link" href="/">{{template "icon-arrow-left" "me-1"}}Back to dashboard</a>
{{if .Debug}}
+3 -3
View File
@@ -16,7 +16,7 @@
</p>
<code class="pk">{{.Repeater.PublicKeyHex}}</code>
<div id="unsupported" class="alert alert-danger mt-3" hidden>
<div id="unsupported" class="alert alert-danger mt-3" role="alert" hidden>
This browser does not support WebSerial. Use Chrome, Edge, or another Chromium-based browser over HTTPS or on localhost.
</div>
@@ -24,9 +24,9 @@
<div class="row mt-3">
<div class="col-12 col-md-8">
<ul id="log" class="evlog console"></ul>
<ul id="log" class="evlog console" role="log" aria-live="polite" aria-label="Console activity"></ul>
<form id="cmdform" class="input-group mt-3" autocomplete="off" hidden>
<input type="text" id="cmdinput" class="form-control font-monospace" placeholder="type a command, e.g. ver">
<input type="text" id="cmdinput" class="form-control font-monospace" placeholder="type a command, e.g. ver" aria-label="Command to send">
<button type="submit" id="cmdsend" class="btn btn-primary">Send</button>
</form>
</div>
+1 -1
View File
@@ -146,7 +146,7 @@
{{if .Mapped}}
<div class="card-body p-2">
<link rel="stylesheet" href="/static/leaflet.css">
<div id="map" style="height:260px"></div>
<div id="map" style="height:260px" role="region" aria-label="Map of your repeater locations"></div>
<script src="/static/leaflet.js"></script>
<script src="/static/meshmap.js"></script>
<script>
+7 -7
View File
@@ -16,11 +16,11 @@
<form method="post" action="/repeaters/{{.Repeater.PublicID}}/edit" class="mt-3">
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="name" value="{{.Repeater.Name}}" required>
<label class="form-label" for="name">Name</label>
<input type="text" class="form-control" id="name" name="name" value="{{.Repeater.Name}}" required>
</div>
<div class="mb-3">
<label class="form-label">Region preset</label>
<label class="form-label" for="region">Region preset</label>
<select class="form-select" id="region" onchange="applyRegion()">
{{range .Presets}}
<option value="{{.ID}}" data-freq="{{.FreqHz}}" data-bw="{{.BwHz}}" data-sf="{{.SF}}" data-cr="{{.CR}}"{{if eq .ID $.SelectedPreset}} selected{{end}}>{{.Label}}</option>
@@ -29,10 +29,10 @@
</select>
</div>
<div class="row g-2">
<div class="col-6 col-md-3"><label class="form-label">Freq (Hz)</label><input type="number" class="form-control" id="radio_freq_hz" name="radio_freq_hz" value="{{.Repeater.RadioFreqHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label">Bandwidth (Hz)</label><input type="number" class="form-control" id="radio_bw_hz" name="radio_bw_hz" value="{{.Repeater.RadioBwHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label">SF</label><input type="number" class="form-control" id="radio_sf" name="radio_sf" value="{{.Repeater.RadioSF}}" min="5" max="12" required></div>
<div class="col-6 col-md-3"><label class="form-label">CR</label><input type="number" class="form-control" id="radio_cr" name="radio_cr" value="{{.Repeater.RadioCR}}" min="5" max="8" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_freq_hz">Freq (Hz)</label><input type="number" class="form-control" id="radio_freq_hz" name="radio_freq_hz" value="{{.Repeater.RadioFreqHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_bw_hz">Bandwidth (Hz)</label><input type="number" class="form-control" id="radio_bw_hz" name="radio_bw_hz" value="{{.Repeater.RadioBwHz}}" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_sf">SF</label><input type="number" class="form-control" id="radio_sf" name="radio_sf" value="{{.Repeater.RadioSF}}" min="5" max="12" required></div>
<div class="col-6 col-md-3"><label class="form-label" for="radio_cr">CR</label><input type="number" class="form-control" id="radio_cr" name="radio_cr" value="{{.Repeater.RadioCR}}" min="5" max="8" required></div>
</div>
<label class="form-check mt-3">
+25 -25
View File
@@ -1,27 +1,27 @@
{{/* Tabler inline SVG icons. Render with {{template "icon-NAME" .}}; the dot is
an optional extra class string appended to the default "icon" class. */}}
{{define "icon-tower"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M16.616 13.924a5 5 0 1 0 -9.23 0"/><path d="M20.307 15.469a9 9 0 1 0 -16.615 0"/><path d="M9 21l3 -9l3 9"/><path d="M10 19h4"/></svg>{{end}}
{{define "icon-home"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M5 12l-2 0l9 -9l9 9l-2 0"/><path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"/><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"/></svg>{{end}}
{{define "icon-plus"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M12 5l0 14"/><path d="M5 12l14 0"/></svg>{{end}}
{{define "icon-search"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"/><path d="M21 21l-6 -6"/></svg>{{end}}
{{define "icon-terminal"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M8 9l3 3l-3 3"/><path d="M13 15l3 0"/><path d="M3 4m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"/></svg>{{end}}
{{define "icon-pencil"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4"/><path d="M13.5 6.5l4 4"/></svg>{{end}}
{{define "icon-antenna"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M6 18l0 -3"/><path d="M10 18l0 -6"/><path d="M14 18l0 -9"/><path d="M18 18l0 -12"/></svg>{{end}}
{{define "icon-list"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M13 5h8"/><path d="M13 9h5"/><path d="M13 15h8"/><path d="M13 19h5"/><path d="M3 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/><path d="M3 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/></svg>{{end}}
{{define "icon-share"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M6 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M18 6m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M8.7 10.7l6.6 -3.4"/><path d="M8.7 13.3l6.6 3.4"/></svg>{{end}}
{{define "icon-trash"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M4 7l16 0"/><path d="M10 11l0 6"/><path d="M14 11l0 6"/><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"/><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"/></svg>{{end}}
{{define "icon-settings"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"/><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/></svg>{{end}}
{{define "icon-world"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"/><path d="M3.6 9h16.8"/><path d="M3.6 15h16.8"/><path d="M11.5 3a17 17 0 0 0 0 18"/><path d="M12.5 3a17 17 0 0 1 0 18"/></svg>{{end}}
{{define "icon-users"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M9 7m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"/><path d="M3 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/><path d="M21 21v-2a4 4 0 0 0 -3 -3.85"/></svg>{{end}}
{{define "icon-user"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"/><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"/></svg>{{end}}
{{define "icon-logout"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M14 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2"/><path d="M9 12h12l-3 -3"/><path d="M18 15l3 -3"/></svg>{{end}}
{{define "icon-login"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M15 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2"/><path d="M21 12h-13l3 -3"/><path d="M11 15l-3 -3"/></svg>{{end}}
{{define "icon-plug"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M7 12l5 5l-1.5 1.5a3.536 3.536 0 1 1 -5 -5l1.5 -1.5z"/><path d="M17 12l-5 -5l1.5 -1.5a3.536 3.536 0 1 1 5 5l-1.5 1.5z"/><path d="M3 21l2.5 -2.5"/><path d="M18.5 5.5l2.5 -2.5"/><path d="M10 11l-2 2"/><path d="M13 14l-2 2"/></svg>{{end}}
{{define "icon-arrow-left"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M5 12l14 0"/><path d="M5 12l6 6"/><path d="M5 12l6 -6"/></svg>{{end}}
{{define "icon-check"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M5 12l5 5l10 -10"/></svg>{{end}}
{{define "icon-alert"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M12 9v4"/><path d="M12 17h.01"/><path d="M10.24 3.957l-8.422 14.06a1.989 1.989 0 0 0 1.7 2.983h16.845a1.989 1.989 0 0 0 1.7 -2.983l-8.423 -14.06a1.989 1.989 0 0 0 -3.4 0z"/></svg>{{end}}
{{define "icon-map-pin"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M9 11a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/><path d="M17.657 16.657l-4.243 4.243a2 2 0 0 1 -2.827 0l-4.244 -4.243a8 8 0 1 1 11.314 0z"/></svg>{{end}}
{{define "icon-key"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.476a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.476a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z"/><path d="M15 9h.01"/></svg>{{end}}
{{define "icon-eye"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"/><path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6"/></svg>{{end}}
{{define "icon-eye-off"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M10.585 10.587a2 2 0 0 0 2.829 2.828"/><path d="M16.681 16.673a8.717 8.717 0 0 1 -4.681 1.327c-3.6 0 -6.6 -2 -9 -6c1.272 -2.12 2.712 -3.678 4.32 -4.674m2.86 -1.146a9.055 9.055 0 0 1 1.82 -.18c3.6 0 6.6 2 9 6c-.666 1.11 -1.379 2.067 -2.138 2.87"/><path d="M3 3l18 18"/></svg>{{end}}
{{define "icon-copy"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon{{if .}} {{.}}{{end}}"><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"/><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"/></svg>{{end}}
{{define "icon-tower"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M16.616 13.924a5 5 0 1 0 -9.23 0"/><path d="M20.307 15.469a9 9 0 1 0 -16.615 0"/><path d="M9 21l3 -9l3 9"/><path d="M10 19h4"/></svg>{{end}}
{{define "icon-home"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M5 12l-2 0l9 -9l9 9l-2 0"/><path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"/><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"/></svg>{{end}}
{{define "icon-plus"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M12 5l0 14"/><path d="M5 12l14 0"/></svg>{{end}}
{{define "icon-search"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"/><path d="M21 21l-6 -6"/></svg>{{end}}
{{define "icon-terminal"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M8 9l3 3l-3 3"/><path d="M13 15l3 0"/><path d="M3 4m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"/></svg>{{end}}
{{define "icon-pencil"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4"/><path d="M13.5 6.5l4 4"/></svg>{{end}}
{{define "icon-antenna"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M6 18l0 -3"/><path d="M10 18l0 -6"/><path d="M14 18l0 -9"/><path d="M18 18l0 -12"/></svg>{{end}}
{{define "icon-list"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M13 5h8"/><path d="M13 9h5"/><path d="M13 15h8"/><path d="M13 19h5"/><path d="M3 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/><path d="M3 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/></svg>{{end}}
{{define "icon-share"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M6 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M18 6m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M8.7 10.7l6.6 -3.4"/><path d="M8.7 13.3l6.6 3.4"/></svg>{{end}}
{{define "icon-trash"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M4 7l16 0"/><path d="M10 11l0 6"/><path d="M14 11l0 6"/><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"/><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"/></svg>{{end}}
{{define "icon-settings"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"/><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/></svg>{{end}}
{{define "icon-world"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"/><path d="M3.6 9h16.8"/><path d="M3.6 15h16.8"/><path d="M11.5 3a17 17 0 0 0 0 18"/><path d="M12.5 3a17 17 0 0 1 0 18"/></svg>{{end}}
{{define "icon-users"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M9 7m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"/><path d="M3 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/><path d="M21 21v-2a4 4 0 0 0 -3 -3.85"/></svg>{{end}}
{{define "icon-user"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"/><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"/></svg>{{end}}
{{define "icon-logout"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M14 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2"/><path d="M9 12h12l-3 -3"/><path d="M18 15l3 -3"/></svg>{{end}}
{{define "icon-login"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M15 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2"/><path d="M21 12h-13l3 -3"/><path d="M11 15l-3 -3"/></svg>{{end}}
{{define "icon-plug"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M7 12l5 5l-1.5 1.5a3.536 3.536 0 1 1 -5 -5l1.5 -1.5z"/><path d="M17 12l-5 -5l1.5 -1.5a3.536 3.536 0 1 1 5 5l-1.5 1.5z"/><path d="M3 21l2.5 -2.5"/><path d="M18.5 5.5l2.5 -2.5"/><path d="M10 11l-2 2"/><path d="M13 14l-2 2"/></svg>{{end}}
{{define "icon-arrow-left"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M5 12l14 0"/><path d="M5 12l6 6"/><path d="M5 12l6 -6"/></svg>{{end}}
{{define "icon-check"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M5 12l5 5l10 -10"/></svg>{{end}}
{{define "icon-alert"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M12 9v4"/><path d="M12 17h.01"/><path d="M10.24 3.957l-8.422 14.06a1.989 1.989 0 0 0 1.7 2.983h16.845a1.989 1.989 0 0 0 1.7 -2.983l-8.423 -14.06a1.989 1.989 0 0 0 -3.4 0z"/></svg>{{end}}
{{define "icon-map-pin"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M9 11a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/><path d="M17.657 16.657l-4.243 4.243a2 2 0 0 1 -2.827 0l-4.244 -4.243a8 8 0 1 1 11.314 0z"/></svg>{{end}}
{{define "icon-key"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.476a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.476a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z"/><path d="M15 9h.01"/></svg>{{end}}
{{define "icon-eye"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"/><path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6"/></svg>{{end}}
{{define "icon-eye-off"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M10.585 10.587a2 2 0 0 0 2.829 2.828"/><path d="M16.681 16.673a8.717 8.717 0 0 1 -4.681 1.327c-3.6 0 -6.6 -2 -9 -6c1.272 -2.12 2.712 -3.678 4.32 -4.674m2.86 -1.146a9.055 9.055 0 0 1 1.82 -.18c3.6 0 6.6 2 9 6c-.666 1.11 -1.379 2.067 -2.138 2.87"/><path d="M3 3l18 18"/></svg>{{end}}
{{define "icon-copy"}}<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="icon{{if .}} {{.}}{{end}}"><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"/><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"/></svg>{{end}}
+2 -2
View File
@@ -15,9 +15,9 @@
<a href="/orgs" class="btn btn-lg">Browse organizations</a>
</div>
</div>
<div class="col-lg-5 d-none d-lg-flex justify-content-center">
<div class="col-lg-5 d-none d-lg-flex justify-content-center" aria-hidden="true">
<span class="text-primary" style="opacity:.85">
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="220" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M16.616 13.924a5 5 0 1 0 -9.23 0"/><path d="M20.307 15.469a9 9 0 1 0 -16.615 0"/><path d="M9 21l3 -9l3 9"/><path d="M10 19h4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="220" height="220" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M16.616 13.924a5 5 0 1 0 -9.23 0"/><path d="M20.307 15.469a9 9 0 1 0 -16.615 0"/><path d="M9 21l3 -9l3 9"/><path d="M10 19h4"/></svg>
</span>
</div>
</div>
+3 -3
View File
@@ -8,7 +8,7 @@
<form method="post" action="/login/password" id="signin-form">
{{if .Next}}<input type="hidden" name="next" value="{{.Next}}">{{end}}
<div class="mb-3">
<label class="form-label">Username</label>
<label class="form-label" for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" autocomplete="username webauthn" autocapitalize="none" placeholder="your-username" required>
</div>
@@ -20,11 +20,11 @@
<div class="hr-text">or use your password</div>
<div class="mb-3">
<label class="form-label">Password</label>
<label class="form-label" for="password">Password</label>
<div class="input-group input-group-flat">
<input type="password" class="form-control" id="password" name="password" autocomplete="current-password" placeholder="Your password" required>
<span class="input-group-text">
<a href="#" class="link-secondary" title="Show password" data-pwtoggle="password" aria-label="Show password">{{template "icon-eye" ""}}</a>
<a href="#" class="link-secondary" role="button" aria-pressed="false" title="Show password" data-pwtoggle="password" aria-label="Show password">{{template "icon-eye" ""}}</a>
</span>
</div>
</div>
+2 -2
View File
@@ -19,8 +19,8 @@
</p>
<form method="post" action="/orgs">
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="name" maxlength="80" placeholder="e.g. Bay Area Mesh" required autofocus>
<label class="form-label" for="org_name">Name</label>
<input type="text" class="form-control" id="org_name" name="name" maxlength="80" placeholder="e.g. Bay Area Mesh" required autofocus>
</div>
<div class="btn-list">
<button type="submit" class="btn btn-primary">Create organization</button>
+11 -11
View File
@@ -74,7 +74,7 @@
<div class="card-header"><h3 class="card-title">Repeater map</h3></div>
<div class="card-body p-2">
<link rel="stylesheet" href="/static/leaflet.css">
<div id="map" style="height:300px"></div>
<div id="map" style="height:300px" role="region" aria-label="Map of organization repeater locations"></div>
<script src="/static/leaflet.js"></script>
<script src="/static/meshmap.js"></script>
<script>
@@ -147,25 +147,25 @@
<div class="card-body">
<form method="post" action="/orgs/{{.Org.Slug}}/edit">
<div class="mb-3">
<label class="form-label">Name</label>
<input type="text" class="form-control" name="name" value="{{.Org.Name}}" maxlength="80" required>
<label class="form-label" for="org_name">Name</label>
<input type="text" class="form-control" id="org_name" name="name" value="{{.Org.Name}}" maxlength="80" required>
</div>
<div class="mb-3">
<label class="form-label">URL slug</label>
<label class="form-label" for="org_slug">URL slug</label>
<div class="input-group">
<span class="input-group-text">/orgs/</span>
<input type="text" class="form-control font-monospace" name="slug" value="{{.Org.Slug}}"
<input type="text" class="form-control font-monospace" id="org_slug" name="slug" value="{{.Org.Slug}}"
pattern="[a-z0-9]+(-[a-z0-9]+)*" minlength="3" maxlength="40" required>
</div>
<small class="form-hint">Lowercase letters, numbers, and hyphens. Changing this changes the org's URL.</small>
</div>
<div class="mb-3">
<label class="form-label">Region <span class="text-secondary">(optional)</span></label>
<input type="text" class="form-control" name="region" value="{{.Org.Region}}" maxlength="120" placeholder="e.g. Buffalo, NY">
<label class="form-label" for="org_region">Region <span class="text-secondary">(optional)</span></label>
<input type="text" class="form-control" id="org_region" name="region" value="{{.Org.Region}}" maxlength="120" placeholder="e.g. Buffalo, NY">
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control" name="description" rows="3" maxlength="2000" placeholder="What this organization is about, where it operates, etc.">{{.Org.Description}}</textarea>
<label class="form-label" for="org_description">Description</label>
<textarea class="form-control" id="org_description" name="description" rows="3" maxlength="2000" placeholder="What this organization is about, where it operates, etc.">{{.Org.Description}}</textarea>
</div>
<button type="submit" class="btn btn-primary">Save profile</button>
</form>
@@ -213,8 +213,8 @@
{{end}}
<form method="post" action="/orgs/{{.Org.Slug}}/domains">
<div class="mb-3">
<label class="form-label">Add a domain</label>
<input type="text" class="form-control font-monospace" name="hostname" placeholder="mesh.example.org" maxlength="253">
<label class="form-label" for="add_domain">Add a domain</label>
<input type="text" class="form-control font-monospace" id="add_domain" name="hostname" placeholder="mesh.example.org" maxlength="253">
</div>
<button type="submit" class="btn btn-primary">Add domain</button>
</form>
+2 -2
View File
@@ -31,8 +31,8 @@
</fieldset>
{{end}}
<div class="mb-3">
<label class="form-label">Change note <span class="text-secondary">(shown to owners when they re-consent)</span></label>
<input type="text" class="form-control" name="note" maxlength="200" placeholder="e.g. added set tx for the new region plan">
<label class="form-label" for="change_note">Change note <span class="text-secondary">(shown to owners when they re-consent)</span></label>
<input type="text" class="form-control" id="change_note" name="note" maxlength="200" placeholder="e.g. added set tx for the new region plan">
</div>
<div class="btn-list">
<button type="submit" class="btn btn-primary">Publish new version</button>
+1 -1
View File
@@ -74,7 +74,7 @@
{{if .HasMap}}
<div class="card-body p-2">
<link rel="stylesheet" href="/static/leaflet.css">
<div id="map" style="height:340px"></div>
<div id="map" style="height:340px" role="region" aria-label="Map of organization repeater locations"></div>
<script src="/static/leaflet.js"></script>
<script src="/static/meshmap.js"></script>
<script>
+1 -1
View File
@@ -121,7 +121,7 @@
<div class="card-header"><h3 class="card-title">Location</h3></div>
<div class="card-body p-2">
<link rel="stylesheet" href="/static/leaflet.css">
<div id="map" style="height:240px"></div>
<div id="map" style="height:240px" role="region" aria-label="Map of this repeater's location"></div>
<script src="/static/leaflet.js"></script>
<script src="/static/meshmap.js"></script>
<script>
+2 -2
View File
@@ -77,8 +77,8 @@
<p class="text-secondary">Each link works once. Mint one per person and label it so you remember who it's for.</p>
<form method="post" action="/repeaters/{{.Repeater.PublicID}}/share/link">
<div class="mb-3">
<label class="form-label">Description <span class="text-secondary">(optional)</span></label>
<input type="text" class="form-control" name="description" placeholder="e.g. for Bob's modem" maxlength="100">
<label class="form-label" for="share_description">Description <span class="text-secondary">(optional)</span></label>
<input type="text" class="form-control" id="share_description" name="description" placeholder="e.g. for Bob's modem" maxlength="100">
</div>
<button type="submit" class="btn btn-primary">Create single-use link</button>
</form>
+4 -4
View File
@@ -9,11 +9,11 @@
<form method="post" action="/signup/password" id="signup-form">
{{if .Next}}<input type="hidden" name="next" value="{{.Next}}">{{end}}
<div class="mb-3">
<label class="form-label">Username</label>
<label class="form-label" for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" autocomplete="username webauthn" autocapitalize="none" placeholder="your-username" required>
</div>
<div class="mb-3">
<label class="form-label">Display name <span class="form-label-description">optional</span></label>
<label class="form-label" for="displayName">Display name <span class="form-label-description">optional</span></label>
<input type="text" class="form-control" id="displayName" name="display_name" autocomplete="off" placeholder="Shown to people you share with">
</div>
@@ -25,11 +25,11 @@
<div class="hr-text">or use a password</div>
<div class="mb-3">
<label class="form-label">Password <span class="form-label-description">min 8 characters</span></label>
<label class="form-label" for="password">Password <span class="form-label-description">min 8 characters</span></label>
<div class="input-group input-group-flat">
<input type="password" class="form-control" id="password" name="password" autocomplete="new-password" minlength="8" placeholder="Choose a password" required>
<span class="input-group-text">
<a href="#" class="link-secondary" title="Show password" data-pwtoggle="password" aria-label="Show password">{{template "icon-eye" ""}}</a>
<a href="#" class="link-secondary" role="button" aria-pressed="false" title="Show password" data-pwtoggle="password" aria-label="Show password">{{template "icon-eye" ""}}</a>
</span>
</div>
</div>