d170a2aecf
gates / gates (push) Successful in 52s
- style A (operator 2026-10-16): hero, tab bar and every section are panels on --bg-1, one width, 16px apart; the install page's form groups use the same rule; the settings tab's 700px inner wrapper is gone - „Belépés az alkalmazásba": name + password side by side, fetched on demand (R-254, R-709); pairing in app_login.go - the empty-value rule: no stored value -> a sentence, never an eye; page and reveal endpoint share storedValuePresent - „Cím és kulcsok" is rows (no Domain input, no disabled storage select, no install form); hint under the title - tests red-proved: app_page_tuning_test.go (4); 36 app-page parity fixtures re-captured, 4 new; install fixtures unchanged Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
80 lines
2.7 KiB
HTML
80 lines
2.7 KiB
HTML
{{define "app_logs"}}
|
|
{{template "layout_start" .}}
|
|
<div class="app-page app-page-wide">
|
|
{{template "app_shell_start" .}}
|
|
|
|
{{/* „NAPLO" — today's logs page inside the shell. Its live poller runs ONLY on this tab, which is
|
|
the reason the tabs are four URLs and not four hidden panels: a poller on the settings tab
|
|
would ask the box for 200 log lines every three seconds for nothing. */}}
|
|
|
|
<div class="app-section" data-tab-panel="logs">
|
|
<div class="logs-container" id="logs-container">
|
|
<pre class="logs-output" id="logs-output">{{.Logs}}</pre>
|
|
</div>
|
|
<div class="logs-actions">
|
|
<span class="logs-live-indicator" id="live-indicator">
|
|
<span class="logs-live-dot"></span> {{T "logs.elo"}}
|
|
</span>
|
|
<button class="btn btn-outline btn-sm" id="logs-toggle" onclick="toggleLive()">{{T "logs.szuneteltetes"}}</button>
|
|
<button class="btn btn-outline btn-sm" onclick="fetchLogs()">{{T "logs.frissites"}}</button>
|
|
</div>
|
|
<script>
|
|
(function() {
|
|
var container = document.getElementById('logs-container');
|
|
var output = document.getElementById('logs-output');
|
|
var indicator = document.getElementById('live-indicator');
|
|
var toggleBtn = document.getElementById('logs-toggle');
|
|
var live = true;
|
|
var timer = null;
|
|
var appSlug = '{{.Meta.Slug}}';
|
|
|
|
function isAtBottom() {
|
|
return container.scrollHeight - container.scrollTop - container.clientHeight < 50;
|
|
}
|
|
|
|
window.fetchLogs = function() {
|
|
fetch('/apps/' + appSlug + '/logs?raw=1')
|
|
.then(function(r) { return r.text(); })
|
|
.then(function(text) {
|
|
var wasAtBottom = isAtBottom();
|
|
output.textContent = text;
|
|
if (wasAtBottom) container.scrollTop = container.scrollHeight;
|
|
})
|
|
.catch(function() {});
|
|
};
|
|
|
|
function startPolling() {
|
|
if (timer) return;
|
|
timer = setInterval(window.fetchLogs, 3000);
|
|
}
|
|
|
|
function stopPolling() {
|
|
if (timer) { clearInterval(timer); timer = null; }
|
|
}
|
|
|
|
window.toggleLive = function() {
|
|
live = !live;
|
|
if (live) {
|
|
startPolling();
|
|
indicator.className = 'logs-live-indicator';
|
|
indicator.innerHTML = '<span class="logs-live-dot"></span> {{T "logs.elo"}}';
|
|
toggleBtn.textContent = '{{T "logs.szuneteltetes"}}';
|
|
} else {
|
|
stopPolling();
|
|
indicator.className = 'logs-live-indicator logs-live-paused';
|
|
indicator.innerHTML = '{{T "logs.szunetelve"}}';
|
|
toggleBtn.textContent = '{{T "logs.folytatas"}}';
|
|
}
|
|
};
|
|
|
|
// Auto-scroll to bottom on initial load
|
|
container.scrollTop = container.scrollHeight;
|
|
startPolling();
|
|
})();
|
|
</script>
|
|
</div>
|
|
|
|
</div>{{/* .app-page */}}
|
|
{{template "layout_end" .}}
|
|
{{end}}
|