feat(master): Ledger UI for Services and Agents (#18)
Restyles the master UI after the Ledger design: a sidebar with live counts, a machine filter, a Live box and a light/dark toggle; the overview groups services per Swarm cluster and machine with icons, search and the Icon / Icon + text / Text switch; the agents page gets a revoke popover and an add-agent panel with a connect step. Jobs, Job and Service pages get the new shell and tokens only. - fonts and Simple Icons are served by the master (no CDN) - the overview reads agents too, to show offline banners and cluster managers - revoking an agent also publishes the agents event - the SSE keepalive is a named event (ping) the Live box counts - the master passes its build version to the UI so agents on another version are marked old Claude-Session: https://claude.ai/code/session_01GAZsp3P7Qto6JnhipxWUJQ
This commit is contained in:
@@ -1,23 +1,62 @@
|
||||
{{define "layout"}}<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="app">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="dark light">
|
||||
<title>{{.Title}} · package-updater</title>
|
||||
<script>
|
||||
// Sets the theme and the display mode before first paint. app.js changes them later.
|
||||
(function () {
|
||||
var root = document.documentElement, theme = null, display = null;
|
||||
try { theme = localStorage.getItem('pu-theme'); display = localStorage.getItem('pu-display'); } catch (e) {}
|
||||
if (theme !== 'light' && theme !== 'dark') {
|
||||
theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
||||
}
|
||||
if (theme === 'light') root.classList.add('t-light');
|
||||
root.classList.add(display === 'icon' || display === 'text' ? 'd-' + display : 'd-both');
|
||||
})();
|
||||
</script>
|
||||
<link rel="preload" href="/static/schibsted-grotesk-latin-wght-normal.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="/static/app.css">
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<script src="/static/htmx-ext-sse.js"></script>
|
||||
<script src="/static/app.js" defer></script>
|
||||
</head>
|
||||
<body hx-headers='{"X-CSRF-Token": "{{.CSRF}}"}' hx-ext="sse" sse-connect="/events">
|
||||
<nav>
|
||||
<a href="/">Services</a>
|
||||
<a href="/jobs">Jobs</a>
|
||||
<a href="/agents">Agents</a>
|
||||
<form method="post" action="/auth/logout">
|
||||
<input type="hidden" name="csrf_token" value="{{.CSRF}}">
|
||||
<button type="submit">Log out</button>
|
||||
</form>
|
||||
</nav>
|
||||
<main>{{template "content" .}}</main>
|
||||
<div class="shell">
|
||||
<aside class="side">
|
||||
<div class="brand">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><path d="M3 6h14M3 10h9M3 14h6"></path><path d="M15 16V10m-2.5 2.5L15 10l2.5 2.5"></path></svg>
|
||||
package-updater
|
||||
</div>
|
||||
<div id="snav" hx-get="/fragments/nav?section={{.Nav.Section}}" hx-trigger="sse:services, sse:agents" hx-swap="innerHTML">{{template "nav" .}}</div>
|
||||
{{block "machines" .}}{{end}}
|
||||
<div class="livebox" id="live">
|
||||
<b><span class="pulse" aria-hidden="true"></span>Live</b>
|
||||
<span id="live-age">connecting…</span>
|
||||
</div>
|
||||
<div class="sfoot">
|
||||
<button type="button" class="btn sm quiet" id="theme-toggle">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><circle cx="7" cy="7" r="5"></circle><path d="M7 2v10" ></path></svg>
|
||||
Theme
|
||||
</button>
|
||||
<form method="post" action="/auth/logout">
|
||||
<input type="hidden" name="csrf_token" value="{{.CSRF}}">
|
||||
<button type="submit" class="btn sm quiet">Log out</button>
|
||||
</form>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="work">
|
||||
<main class="main">{{template "content" .}}</main>
|
||||
{{block "drawer" .}}{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>{{end}}
|
||||
|
||||
{{define "nav"}}<nav class="snav" aria-label="Main">
|
||||
<a href="/"{{if eq .Nav.Section "services"}} class="cur" aria-current="page"{{end}}>Services <span class="n">{{.Nav.Services}}</span></a>
|
||||
<a href="/jobs"{{if eq .Nav.Section "jobs"}} class="cur" aria-current="page"{{end}}>Jobs</a>
|
||||
<a href="/agents"{{if eq .Nav.Section "agents"}} class="cur" aria-current="page"{{end}}>Agents <span class="n">{{.Nav.AgentsOnline}}/{{.Nav.AgentsTotal}}</span></a>
|
||||
</nav>{{end}}
|
||||
|
||||
Reference in New Issue
Block a user