feat: one-page panel - shell with Машины, Терминал and Настройки, pages as fragments, forms sent in place

This commit is contained in:
redtea committed 2026-10-15 20:05:20 +03:00
1 parent 7b9daa524e
commit 113c63fd1b
17 files changed
+678 -432

No files matched your search

+2 -48
View File
@@ -1,12 +1,4 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Настройки - LPRA</title>
<link rel="stylesheet" href="{{asset "app.css"}}">
<script src="{{asset "pin.js"}}"></script>
<script src="{{asset "scroll.js"}}" defer></script>
{{define "settings-body"}}<div class="page" data-module="settings">
<style>
/* An install command, with a button to copy it at its right. */
/* Users: role, machines, password sign-in. */
@@ -49,15 +41,6 @@
.codeform input[type=number] { width:72px; height:30px; padding:4px 8px; }
.codeform .ico { display:inline-flex; align-items:center; gap:6px; height:30px; }
</style>
</head>
<body>
<header class="topbar">
<div class="brand"><a class="home" href="/" title="К списку машин">LPRA</a> · Удалённый доступ</div>
<nav>
<a href="/">Машины</a>
<button type="button" class="btn ghost icon acct-btn" id="acct" title="Аккаунт" aria-label="Аккаунт"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg></button>
</nav>
</header>
<div class="wrap">
<h1>Настройки</h1>
{{if .Error}}<div class="err">{{.Error}}</div>{{end}}
@@ -280,33 +263,4 @@
<p class="muted" style="font-size:12px; text-align:center">LPRA сервер · версия {{.ServerVersion}}</p>
</div>
<script>
// Copy an install command: the clipboard API on https, the old way otherwise.
(function () {
var CHECK = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
function copy(text) {
if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text);
var ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0';
document.body.appendChild(ta);
ta.select();
var ok = document.execCommand('copy');
ta.remove();
return ok ? Promise.resolve() : Promise.reject(new Error('нет доступа к буферу'));
}
document.querySelectorAll('.copybox .copy').forEach(function (b) {
var icon = b.innerHTML;
b.onclick = function () {
copy(document.getElementById(b.dataset.copy).textContent.trim()).then(function () {
b.innerHTML = CHECK; b.classList.add('done'); b.title = 'Скопировано';
setTimeout(function () { b.innerHTML = icon; b.classList.remove('done'); b.title = 'Скопировать'; }, 1500);
}, function (e) { alert('Не скопировалось: ' + e.message); });
};
});
})();
</script>
<script src="{{asset "users.js"}}"></script>
<script src="{{asset "account.js"}}"></script>
</body>
</html>
</div>{{end}}