feat(cockpit): dependency-free inline-SVG nav sparkline

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-06-14 11:47:19 +02:00
parent 1d4630aeb6
commit eb211a2e5d
3 changed files with 43 additions and 0 deletions

View File

View File

@@ -0,0 +1,23 @@
"""Dependency-free inline-SVG charts. Server-rendered so the cockpit needs no JS charting library — the SVG
string is embedded directly in the HTML. nav_sparkline maps a NAV series to a polyline scaled to the box."""
from __future__ import annotations
def nav_sparkline(nav: list[float], *, width: int = 120, height: int = 28,
color: str = "#3fb950") -> str:
box = f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" '\
'xmlns="http://www.w3.org/2000/svg">'
if not nav:
return box + "</svg>"
lo, hi = min(nav), max(nav)
span = (hi - lo) or 1.0
n = len(nav)
step = width / (n - 1) if n > 1 else 0.0
pts = []
for i, v in enumerate(nav):
x = i * step
y = height - (v - lo) / span * (height - 2) - 1 # 1px padding, y inverted
pts.append(f"{x:.1f},{y:.1f}")
stroke = color if nav[-1] >= nav[0] else "#f85149"
return (box + f'<polyline fill="none" stroke="{stroke}" stroke-width="1.5" '
f'points="{" ".join(pts)}" /></svg>')

20
tests/unit/test_charts.py Normal file
View File

@@ -0,0 +1,20 @@
"""nav_sparkline renders a self-contained inline SVG polyline from a NAV series (no JS, no external libs)."""
from __future__ import annotations
from fxhnt.adapters.web.charts import nav_sparkline
def test_sparkline_is_svg_with_points() -> None:
svg = nav_sparkline([1.0, 1.01, 1.005, 1.02], width=100, height=20)
assert svg.startswith("<svg") and svg.rstrip().endswith("</svg>")
assert "<polyline" in svg and "points=" in svg
def test_sparkline_empty_series_is_safe() -> None:
svg = nav_sparkline([], width=100, height=20)
assert svg.startswith("<svg") and "polyline" not in svg
def test_sparkline_flat_series_does_not_divide_by_zero() -> None:
svg = nav_sparkline([1.0, 1.0, 1.0], width=100, height=20)
assert "<polyline" in svg # flat line, no exception