v0.319.0: the app page in panels, the login in one place, a password the box never had is said in words
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
This commit is contained in:
2026-10-16 12:12:06 +02:00
parent 8d44416610
commit d170a2aecf
58 changed files with 6046 additions and 1776 deletions
@@ -14,21 +14,33 @@
Everything here is PLAIN TEXT through html/template. No markdown, no raw HTML: a step that
contains a tag is shown as the characters the author typed. */}}
<!-- Screenshots (graceful — hidden if assets don't exist) -->
<script>
// Defined BEFORE the images: an error event can fire as soon as an <img> is parsed.
function appShotGone(img) {
img.style.display = 'none';
var box = img.parentNode, left = false;
box.querySelectorAll('img').forEach(function (i) { if (i.style.display !== 'none') { left = true; } });
if (!left) { box.closest('.app-section').style.display = 'none'; }
}
</script>
<!-- Screenshots (graceful — each hidden if its asset does not exist, and the section with the last
one, so a panel is never drawn around nothing) -->
<div class="app-section" data-screenshots-section="1">
<div class="app-screenshots" id="screenshots">
<img src="{{screenshotURL .Meta.Slug 1}}" alt="" class="app-screenshot"
onerror="this.style.display='none'">
onerror="appShotGone(this)">
<img src="{{screenshotURL .Meta.Slug 2}}" alt="" class="app-screenshot"
onerror="this.style.display='none'">
onerror="appShotGone(this)">
<img src="{{screenshotURL .Meta.Slug 3}}" alt="" class="app-screenshot"
onerror="this.style.display='none'">
onerror="appShotGone(this)">
</div>
</div>
{{if not .Deployed}}
{{/* The install button lives on this tab, because this tab IS the page for a not-installed app.
The server refuses a non-installable template regardless; hiding the button keeps the page
from offering an action that would be rejected. */}}
<div class="app-section" style="margin-top:1rem">
<div class="app-section">
{{if canInstall .Meta}}
<a href="/apps/{{.Meta.Slug}}/install" class="btn btn-primary" data-install-cta="1"
onclick="return checkBeforeDeploy(event, '{{.Stack.Name}}')">{{T "app_info.telepites"}}</a>
@@ -71,7 +83,7 @@
{{if .AppInfo.Guide.Rendered}}
<div class="app-guide" data-guide="1" data-guide-checked="{{.AppInfo.Guide.CheckedVersion}}">
{{- range .AppInfo.Guide.Sections}}
<div class="app-section" style="margin-top:1rem" data-guide-section="1">
<div class="app-section" data-guide-section="1">
<h3>{{.Title}}</h3>
{{if .Intro}}<p>{{.Intro}}</p>{{end}}
{{if .Steps}}
@@ -82,7 +94,7 @@
</div>
{{- end}}
{{- if .AppInfo.Guide.FAQ}}
<div class="app-section" style="margin-top:1rem" data-guide-faq="1">
<div class="app-section" data-guide-faq="1">
<h3>{{T "app_page.faq"}}</h3>
<dl class="app-faq">
{{- range .AppInfo.Guide.FAQ}}
@@ -94,7 +106,7 @@
{{- end}}
</div>
{{else}}
<p class="form-hint" style="margin-top:1rem" data-guide-missing="1">{{T "app_page.guide_missing"}}</p>
<div class="app-section" data-guide-missing="1"><p>{{T "app_page.guide_missing"}}</p></div>
{{end}}
<div class="app-sections">