feat(hosts): a host's health tiles span the card and the gaps match; Add host is a dialog on the list (#5290, #5291)
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 54s
CI / unit (push) Successful in 1m18s
CI / publish (push) Successful in 1m23s

#5290 "Host page: the health tiles span the card's width; even spacing above Ansible":
the four tiles take equal quarters (halves on a phone) and their sparklines stretch
across them; the Monitors + Agent row drops the Agent card's own margin, so the gap
above Ansible is 1rem like every other.

#5291 "Add host opens as a dialog over the Hosts list": a native <dialog> on /hosts/,
opened by Add host or /hosts/?add=1. A name clash re-renders the list with it open,
the error and what was typed. GET /hosts/new is gone; hosts/form.html is Edit only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 18:22:48 -04:00
co-authored by Claude Opus 5.5
parent bfe3618e88
commit fb20415a38
13 changed files with 136 additions and 52 deletions
+11 -6
View File
@@ -77,11 +77,15 @@ async def collect_status(db) -> list[StatusEntry]:
def sparkline_svg(values: list[float], width: int = 80, height: int = 20,
stroke: str = "#6060c0") -> str:
"""Tiny inline SVG polyline for a response-time series."""
stroke: str = "#6060c0", fill_width: bool = False) -> str:
"""Tiny inline SVG polyline for a response-time series. With fill_width
the line stretches across its container (CSS sets the width) and keeps a
1.5px stroke."""
vals = [v for v in values if v is not None]
size = (f'width="100%" height="{height}" preserveAspectRatio="none"' if fill_width
else f'width="{width}" height="{height}"')
if len(vals) < 2:
return f'<svg width="{width}" height="{height}"></svg>'
return f'<svg {size}></svg>'
mn, mx = min(vals), max(vals)
if mx == mn:
mx = mn + 1.0
@@ -92,10 +96,11 @@ def sparkline_svg(values: list[float], width: int = 80, height: int = 20,
y = height - (v - mn) / (mx - mn) * (height - 2) - 1
pts.append(f"{x:.1f},{y:.1f}")
poly = " ".join(pts)
style = "display:block;" if fill_width else "vertical-align:middle;"
keep = ' vector-effect="non-scaling-stroke"' if fill_width else ""
return (
f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" '
f'style="vertical-align:middle;">'
f'<polyline points="{poly}" fill="none" stroke="{stroke}" stroke-width="1.5"/>'
f'<svg {size} viewBox="0 0 {width} {height}" style="{style}">'
f'<polyline points="{poly}" fill="none" stroke="{stroke}" stroke-width="1.5"{keep}/>'
f'</svg>'
)