feat(host_agent): lazy-load full-metrics charts + live-poll current state
The full-metrics page rendered once server-side with the history query inline, so the charts blocked first paint and nothing refreshed without a reload (it reads the latest snapshot the server holds — the agent pushes ~every 30s). Split it into a shell + two HTMX fragments: - Shell (host_detail.html): header + shared time-range toggle + two containers; paints instantly. - Current state (/<id>/metrics → _host_metrics.html): identity + gauges + per-core + filesystems + interfaces/disks + temps, from the DISTINCT ON latest query. hx-trigger "load, every 15s" → live numbers at ~the agent cadence. - History charts (/<id>/charts → _host_charts.html): the 3 charts + Chart.js, from the date_bin history query. hx-trigger "load, every 60s, rangeChange" so they lazy-load (never block paint), refresh slowly, and follow the range selector. Leak-safe: previous Chart instances are destroyed before re-render. - Range toggle switched from full-reload links to the shared _time_range.html (setTimeRange + rangeChange), so only the fragments refetch. - routes: host_detail (shell) + host_detail_metrics + host_detail_charts, with a _split_host_metrics helper. - tests/test_templates_parse.py now also parses plugin templates (these fragments aren't rendered in the unit lane). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
This commit is contained in:
@@ -11,12 +11,24 @@ import jinja2
|
||||
|
||||
import steward
|
||||
|
||||
_ROOT = pathlib.Path(steward.__file__).parent / "templates"
|
||||
_REPO = pathlib.Path(steward.__file__).parent.parent
|
||||
_CORE_TEMPLATES = _REPO / "steward" / "templates"
|
||||
_PLUGINS = _REPO / "plugins"
|
||||
|
||||
|
||||
def test_all_steward_templates_parse():
|
||||
env = jinja2.Environment()
|
||||
files = sorted(_ROOT.rglob("*.html"))
|
||||
files = sorted(_CORE_TEMPLATES.rglob("*.html"))
|
||||
assert files, "no steward templates found"
|
||||
for f in files:
|
||||
env.parse(f.read_text()) # raises TemplateSyntaxError on a bad tag
|
||||
|
||||
|
||||
def test_all_plugin_templates_parse():
|
||||
# First-party plugins ship templates too (host_agent fragments, docker, …);
|
||||
# they aren't rendered in the unit lane, so parse them here to catch a bad tag.
|
||||
env = jinja2.Environment()
|
||||
files = sorted(_PLUGINS.rglob("templates/**/*.html"))
|
||||
assert files, "no plugin templates found"
|
||||
for f in files:
|
||||
env.parse(f.read_text())
|
||||
|
||||
Reference in New Issue
Block a user