Execute PLAN_FRONTEND.md

This commit is contained in:
2026-03-14 20:42:51 +01:00
parent b9ec67ec00
commit 4369e66dee
9 changed files with 1571 additions and 0 deletions

51
cmd/frontend/sparkline.go Normal file
View File

@@ -0,0 +1,51 @@
package main
import (
"fmt"
"html/template"
"strings"
pb "git.ipng.ch/ipng/nginx-logtail/proto/logtailpb"
)
const (
svgW = 300.0
svgH = 60.0
svgPad = 4.0
)
// renderSparkline converts trend points into an inline SVG polyline.
// Returns "" if there are fewer than 2 points or all counts are zero.
func renderSparkline(points []*pb.TrendPoint) template.HTML {
if len(points) < 2 {
return ""
}
var maxCount int64
for _, p := range points {
if p.Count > maxCount {
maxCount = p.Count
}
}
if maxCount == 0 {
return ""
}
n := len(points)
var pts strings.Builder
for i, p := range points {
x := svgPad + float64(i)*(svgW-2*svgPad)/float64(n-1)
y := svgPad + (svgH-2*svgPad)*(1.0-float64(p.Count)/float64(maxCount))
if i > 0 {
pts.WriteByte(' ')
}
fmt.Fprintf(&pts, "%.1f,%.1f", x, y)
}
return template.HTML(fmt.Sprintf(
`<svg viewBox="0 0 %d %d" width="%d" height="%d" xmlns="http://www.w3.org/2000/svg">`+
`<polyline points="%s" fill="none" stroke="#4a90d9" stroke-width="1.5" stroke-linejoin="round"/>`+
`</svg>`,
int(svgW), int(svgH), int(svgW), int(svgH), pts.String(),
))
}