This commit is contained in:
2026-10-03 20:24:37 +02:00
parent d603d8d574
commit 7094d05030
8 changed files with 581 additions and 135 deletions
+73 -26
View File
@@ -1,34 +1,81 @@
<!DOCTYPE html>
<html lang="pl">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Logowanie</title>
<style>
body { font-family: sans-serif; max-width: 400px; margin: 4rem auto; padding: 0 1rem; }
form { display: flex; flex-direction: column; gap: 0.75rem; }
label { font-weight: bold; }
input { padding: 0.5rem; font-size: 1rem; }
button { padding: 0.6rem; font-size: 1rem; cursor: pointer; }
.error { background: #fee2e2; border: 1px solid #fca5a5; color: #991b1b; padding: 0.5rem 0.75rem; border-radius: 4px; }
</style>
<title>Login — Yggdrasil</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='retro.css') }}">
</head>
<body>
<h1>Logowanie</h1>
{% if error %}
<p class="error">{{ error }}</p>
{% endif %}
<form method="post" action="{{ url_for('login', next=next) if next else url_for('login') }}">
<input type="hidden" name="next" value="{{ next }}">
<div>
<label for="username">Użytkownik:</label><br>
<input type="text" id="username" name="username" required autocomplete="username" style="width: 100%; box-sizing: border-box;">
</div>
<div>
<label for="password">Hasło:</label><br>
<input type="password" id="password" name="password" required autocomplete="current-password" style="width: 100%; box-sizing: border-box;">
</div>
<button type="submit">Zaloguj</button>
</form>
<div class="dialog">
<div class="dialog-title">[ Login ]</div>
<h1>Yggdrasil</h1>
{% if error %}
<p class="error">! {{ error }}</p>
{% endif %}
<form method="post" action="{{ url_for('login', next=next) if next else url_for('login') }}">
<input type="hidden" name="next" value="{{ next }}">
<div>
<label for="username">Username:</label><br>
<div class="term-field">
<input type="text" id="username" name="username" required autocomplete="username" autofocus>
<span class="caret-block" aria-hidden="true"></span>
</div>
</div>
<div>
<label for="password">Password:</label><br>
<div class="term-field">
<input type="password" id="password" name="password" required autocomplete="current-password">
<span class="caret-block" aria-hidden="true"></span>
</div>
</div>
<button type="submit">[ OK ] Log in</button>
</form>
</div>
<script>
// Block caret, Midnight Commander style.
// The native caret is hidden (only when this script runs) and a
// blinking block is drawn exactly where the text cursor is.
// Measurement is exact because the font is monospace.
// Without JS the fields keep their normal native caret.
function attachBlockCaret(input) {
const field = input.closest('.term-field');
const block = field.querySelector('.caret-block');
input.classList.add('js-caret');
const ctx = document.createElement('canvas').getContext('2d');
function place() {
const cs = getComputedStyle(input);
ctx.font = cs.font;
const pos = (input.selectionStart !== null && input.selectionStart !== undefined)
? input.selectionStart
: input.value.length;
let text = input.value.slice(0, pos);
if (input.type === 'password') text = '\u2022'.repeat(text.length);
const padLeft = parseFloat(cs.paddingLeft) || 0;
const borderLeft = parseFloat(cs.borderLeftWidth) || 0;
const x = borderLeft + padLeft + ctx.measureText(text).width - (input.scrollLeft || 0);
block.style.left = Math.max(borderLeft + padLeft, x) + 'px';
}
function show() {
field.classList.add('active');
requestAnimationFrame(place);
}
function hide() { field.classList.remove('active'); }
['input', 'change', 'keyup', 'click', 'select', 'focus'].forEach((ev) => {
input.addEventListener(ev, show);
});
input.addEventListener('blur', hide);
window.addEventListener('load', () => setTimeout(place, 300)); // late autofill
if (document.fonts && document.fonts.ready) document.fonts.ready.then(place);
if (document.activeElement === input) show();
}
document.querySelectorAll('.term-field input').forEach(attachBlockCaret);
</script>
</body>
</html>