From e58c86cf010f690228622a0dc8cae7b921900448 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 17 Jun 2026 16:14:59 -0400 Subject: [PATCH] =?UTF-8?q?feat(dashboard):=20widget=20readability=20?= =?UTF-8?q?=E2=80=94=20name-per-line,=20fill=20graphs,=20container=20break?= =?UTF-8?q?points?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address graphical issues raised from the dashboard screenshot: - No more truncated host names. Hosts-Overview and Host-Agent-Resources put the host name on its own line (full, wraps if needed) with its data grouped beneath it; ping/dns (.ping-name) and uptime widget names wrap instead of ellipsis. Prefer vertical overflow over cramming/truncating a row. - History graph fills the panel: drop the fixed 0–100 y-axis ceiling (beginAtZero + 8% grace) so the lines use the vertical space instead of hugging the bottom; axis labels still show real %. - Container-query breakpoints: the widget body is now a query container, so fragments restyle to their OWN panel width. Host-list widgets flow into 2 cols ≥520px and 3 cols ≥900px (.host-blocks) — use the width, remove vertical deadspace — and collapse to one column when narrow. Mirrored into the share view. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../host_agent/templates/widget_history.html | 5 ++- .../host_agent/templates/widget_table.html | 39 +++++++++++-------- steward/templates/base.html | 10 ++++- steward/templates/dashboard/share_view.html | 6 ++- steward/templates/hosts/overview_widget.html | 39 +++++++++++-------- steward/templates/hosts/uptime_widget.html | 4 +- 6 files changed, 64 insertions(+), 39 deletions(-) diff --git a/plugins/host_agent/templates/widget_history.html b/plugins/host_agent/templates/widget_history.html index cf9f492..5d1a464 100644 --- a/plugins/host_agent/templates/widget_history.html +++ b/plugins/host_agent/templates/widget_history.html @@ -43,7 +43,10 @@ elements: { point: { radius: 0 } }, scales: { x: { type: "linear", ticks: { callback: function (v) { return fmtTime(v); }, maxTicksLimit: 6, color: "#8a8a92", font: { size: 10 } }, grid: { color: "#30303a" } }, - y: { beginAtZero: true, max: 100, ticks: { color: "#8a8a92", font: { size: 10 } }, grid: { color: "#30303a" } }, + // Auto-scale to the data (with a little headroom) so the lines fill the + // panel instead of hugging the bottom of a fixed 0–100 axis. Axis labels + // still show the real %, so the values stay honest. + y: { beginAtZero: true, grace: "8%", ticks: { color: "#8a8a92", font: { size: 10 } }, grid: { color: "#30303a" } }, }, plugins: { legend: { labels: { color: "#b8b8b0", boxWidth: 10, font: { size: 11 } } } }, }, diff --git a/plugins/host_agent/templates/widget_table.html b/plugins/host_agent/templates/widget_table.html index aa5ffc7..a403a0c 100644 --- a/plugins/host_agent/templates/widget_table.html +++ b/plugins/host_agent/templates/widget_table.html @@ -1,9 +1,10 @@ -{# host_agent widget — fleet glance: per-host row with a sparkline beside each - metric (cpu/mem/disk/load), mirroring the host-page AGENT panel. #} +{# host_agent widget — fleet glance. Per host: name on its own line (never + truncated), then a row of metric cells (cpu/mem/disk/load), each value with a + trend sparkline beneath it — the host-page AGENT panel, on the dashboard. #} {% from "_macros.html" import metric_style %} {% if rows %} {% macro metric_cell(label, value, fmt, svg, style="", title="") %} -
+
{{ label }}
{% if value is not none %}{{ fmt|format(value) }}{% else %}—{% endif %} @@ -11,27 +12,31 @@
{{ svg | safe }}
{% endmacro %} -
+
{% for r in rows %} {% set stale = r.stale %} -
- - {% set name_style = "flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500;" %} - {% if session.user_id %} - {{ r.host.name }} - {% else %} - {{ r.host.name }} - {% endif %} -
+
+ {# Line 1 — health dot + full host name (own line) + last seen #} +
+ + {% if session.user_id %} + {{ r.host.name }} + {% else %} + {{ r.host.name }} + {% endif %} + + {{ r.reg.last_seen_at.strftime("%H:%M:%S") if r.reg.last_seen_at else "never" }} + +
+ {# Line 2 — metric cells with value + trend; wraps instead of truncating #} +
{{ metric_cell("cpu", r.cpu_pct, "%.0f%%", r.sparks.cpu, metric_style(r.cpu_pct), "Average CPU utilization") }} {{ metric_cell("mem", r.mem_used_pct, "%.0f%%", r.sparks.mem, metric_style(r.mem_used_pct), "Memory in use") }} {{ metric_cell("disk /", r.disk_root, "%.0f%%", r.sparks.disk, metric_style(r.disk_root), "Root filesystem (/) usage") }} {{ metric_cell("load", r.load_1m, "%.2f", r.sparks.load, "", "Load average (1m)") }}
- - {{ r.reg.last_seen_at.strftime("%H:%M:%S") if r.reg.last_seen_at else "never" }} -
{% endfor %}
diff --git a/steward/templates/base.html b/steward/templates/base.html index 1652153..b6c8012 100644 --- a/steward/templates/base.html +++ b/steward/templates/base.html @@ -163,7 +163,7 @@ textarea { resize: vertical; } .ping-row { display:flex; align-items:center; gap:1rem; padding:0.45rem 0; border-bottom:1px solid var(--border); } .ping-row:last-child { border-bottom:none; } .ping-meta { min-width:120px; max-width:180px; flex-shrink:1; overflow:hidden; } -.ping-name { font-size:0.875rem; font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block; } +.ping-name { font-size:0.875rem; font-weight:500; display:block; overflow-wrap:anywhere; } a.ping-name:hover { text-decoration:underline; } /* Dashboard widget grid — Gridstack-rendered. Static (positioned but inert) in view mode; the dashboard itself becomes the edit surface when Edit is toggled, @@ -174,6 +174,14 @@ a.ping-name:hover { text-decoration:underline; } padding:0.8rem 1rem; display:flex; flex-direction:column; overflow:hidden; } .grid-stack-item-content .dash-cell-body { flex:1; min-height:0; overflow:auto; } +/* The widget body is a query container, so fragments can restyle to their OWN + panel width (not the viewport) — widgets adapt as you resize them. */ +.dash-cell-body { container-type:inline-size; } +/* Host-list widgets flow into columns as the panel widens: use the width, drop + the vertical deadspace; stay single-column (and untruncated) when narrow. */ +.host-blocks { display:grid; grid-template-columns:1fr; gap:0 1.5rem; } +@container (min-width:520px) { .host-blocks { grid-template-columns:1fr 1fr; } } +@container (min-width:900px) { .host-blocks { grid-template-columns:1fr 1fr 1fr; } } .panel-chrome { display:none; } .panel-drag { cursor:grab; color:var(--text-dim); user-select:none; } .panel-drag:active { cursor:grabbing; } diff --git a/steward/templates/dashboard/share_view.html b/steward/templates/dashboard/share_view.html index 67d11c9..d0ed81f 100644 --- a/steward/templates/dashboard/share_view.html +++ b/steward/templates/dashboard/share_view.html @@ -44,7 +44,7 @@ .ping-row { display:flex; align-items:center; gap:1rem; padding:0.45rem 0; border-bottom:1px solid var(--border); } .ping-row:last-child { border-bottom:none; } .ping-meta { min-width:120px; max-width:180px; flex-shrink:1; overflow:hidden; } - .ping-name { font-size:0.875rem; font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:block; } + .ping-name { font-size:0.875rem; font-weight:500; display:block; overflow-wrap:anywhere; } .ping-addr { display:block; color:var(--text-muted); font-size:0.75rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .ping-pills { display:flex; gap:2px; flex:1; min-width:0; overflow:hidden; align-items:center; } .pill { display:inline-block; width:7px; height:22px; border-radius:2px; } @@ -59,6 +59,10 @@ .dash-grid { display:grid; grid-template-columns:repeat(12,1fr); grid-auto-rows:70px; gap:1rem; } .dash-grid > .dash-cell { margin-bottom:0; min-width:0; overflow:hidden; display:flex; flex-direction:column; } .dash-cell .dash-cell-body { flex:1; min-height:0; overflow:auto; } + .dash-cell-body { container-type:inline-size; } + .host-blocks { display:grid; grid-template-columns:1fr; gap:0 1.5rem; } + @container (min-width:520px) { .host-blocks { grid-template-columns:1fr 1fr; } } + @container (min-width:900px) { .host-blocks { grid-template-columns:1fr 1fr 1fr; } } @media (max-width:820px) { .dash-grid { grid-template-columns:1fr; grid-auto-rows:auto; } .dash-grid > .dash-cell { grid-column:1 / -1 !important; grid-row:auto !important; } diff --git a/steward/templates/hosts/overview_widget.html b/steward/templates/hosts/overview_widget.html index 8c2052b..16776e9 100644 --- a/steward/templates/hosts/overview_widget.html +++ b/steward/templates/hosts/overview_widget.html @@ -1,33 +1,38 @@ -{# Unified Hosts widget — monitor status + agent glance per host, links to the hub. #} +{# Unified Hosts widget — monitor status + agent glance per host, links to the hub. + Name sits on its own line (never truncated); its data wraps beneath it, grouped + under the host. Prefer vertical growth over cramming a row. #} {% from "_macros.html" import metric_style %} {% if hosts %} -
+
{% for host in hosts %} {% set ping = latest_pings.get(host.id) %} {% set dns = latest_dns.get(host.id) %} {% set ut = uptime.get(host.id, {}) %} {% set a = agent.get(host.name, {}) %} {% set down = host.ping_enabled and ping and ping.status.value != 'up' %} -
- - {% set name_style = "flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500;" %} - {% if session.user_id %} - {{ host.name }} - {% else %} - {{ host.name }} - {% endif %} - {% set spark = cpu_sparks.get(host.name) %} - {% if spark %}{{ spark | safe }}{% endif %} - - {# Monitors #} + {% set spark = cpu_sparks.get(host.name) %} +
+ {# Line 1 — status dot + full host name (own line) + cpu trend #} +
+ + {% if session.user_id %} + {{ host.name }} + {% else %} + {{ host.name }} + {% endif %} + {% if spark %}{{ spark | safe }}{% endif %} +
+ {# Line 2 — monitors + agent metrics; wraps instead of truncating #} +
{% if host.ping_enabled and ping and ping.response_time_ms is not none %} ping {{ "%.0f"|format(ping.response_time_ms) }}ms {% endif %} {% if ut.get('24h') is not none %} 24h {{ "%.1f"|format(ut['24h']) }}% {% endif %} - {# Agent glance — failing metric coloured amber/red (≥80/≥90) #} {% if a %} {% if a.get('cpu_pct') is not none %}cpu {{ "%.0f"|format(a.cpu_pct) }}%{% endif %} {% if a.get('mem_used_pct') is not none %}mem {{ "%.0f"|format(a.mem_used_pct) }}%{% endif %} @@ -36,7 +41,7 @@ {% else %} no agent {% endif %} - +
{% endfor %}
diff --git a/steward/templates/hosts/uptime_widget.html b/steward/templates/hosts/uptime_widget.html index 50b34ed..52675e4 100644 --- a/steward/templates/hosts/uptime_widget.html +++ b/steward/templates/hosts/uptime_widget.html @@ -17,9 +17,9 @@
{% if session.user_id %} {{ host.name }} + style="overflow-wrap:anywhere;color:inherit;text-decoration:none;">{{ host.name }} {% else %} - {{ host.name }} + {{ host.name }} {% endif %} {% for window in ["24h", "7d", "30d"] %} {% set pct = ut.get(window) %}