87 lines
2.9 KiB
Go
87 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"html"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
func servePreviewStatusSVG(w http.ResponseWriter, label string, status int) {
|
|
w.Header().Set("Content-Type", "image/svg+xml; charset=utf-8")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
|
|
if status <= 0 {
|
|
status = http.StatusOK
|
|
}
|
|
|
|
title := html.EscapeString(strings.TrimSpace(label))
|
|
if title == "" {
|
|
title = "Preview"
|
|
}
|
|
|
|
// 16:9 (passt zu deinen Cards)
|
|
svg := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180" preserveAspectRatio="xMidYMid slice">
|
|
<defs>
|
|
<!-- Subtle gradient background -->
|
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
<stop offset="0" stop-color="rgba(99,102,241,0.10)"/>
|
|
<stop offset="1" stop-color="rgba(14,165,233,0.08)"/>
|
|
</linearGradient>
|
|
|
|
<!-- Soft vignette -->
|
|
<radialGradient id="vig" cx="50%" cy="45%" r="75%">
|
|
<stop offset="0" stop-color="rgba(0,0,0,0)"/>
|
|
<stop offset="1" stop-color="rgba(0,0,0,0.18)"/>
|
|
</radialGradient>
|
|
|
|
<!-- Card shadow -->
|
|
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="rgba(0,0,0,0.18)"/>
|
|
</filter>
|
|
</defs>
|
|
|
|
<!-- base -->
|
|
<rect x="0" y="0" width="320" height="180" rx="18" fill="rgba(17,24,39,0.06)"/>
|
|
<rect x="0" y="0" width="320" height="180" rx="18" fill="url(#bg)"/>
|
|
<rect x="0" y="0" width="320" height="180" rx="18" fill="url(#vig)"/>
|
|
|
|
<!-- inner card -->
|
|
<g filter="url(#shadow)">
|
|
<rect x="18" y="18" width="284" height="144" rx="16"
|
|
fill="rgba(255,255,255,0.72)"
|
|
stroke="rgba(0,0,0,0.08)"/>
|
|
<rect x="18" y="18" width="284" height="144" rx="16"
|
|
fill="rgba(255,255,255,0)"
|
|
stroke="rgba(99,102,241,0.18)"
|
|
stroke-width="2"
|
|
stroke-dasharray="6 6"/>
|
|
</g>
|
|
|
|
<!-- icon -->
|
|
<g transform="translate(160 70)">
|
|
<circle r="20" fill="rgba(17,24,39,0.08)" stroke="rgba(0,0,0,0.08)"/>
|
|
<!-- simple "image-off" icon -->
|
|
<path d="M-10 6 L-4 0 L2 6 L10 -2" fill="none" stroke="rgba(17,24,39,0.55)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
<path d="M-10 -6 H10" fill="none" stroke="rgba(17,24,39,0.35)" stroke-width="2.4" stroke-linecap="round"/>
|
|
<path d="M-12 12 L12 -12" fill="none" stroke="rgba(239,68,68,0.55)" stroke-width="2.6" stroke-linecap="round"/>
|
|
</g>
|
|
|
|
<!-- text -->
|
|
<text x="160" y="118" text-anchor="middle"
|
|
font-family="ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto"
|
|
font-size="16" font-weight="750"
|
|
fill="rgba(17,24,39,0.88)">` + title + `</text>
|
|
|
|
<text x="160" y="140" text-anchor="middle"
|
|
font-family="ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto"
|
|
font-size="11.5" font-weight="650"
|
|
fill="rgba(75,85,99,0.82)">Preview nicht verfügbar</text>
|
|
</svg>
|
|
`
|
|
|
|
w.WriteHeader(status)
|
|
_, _ = w.Write([]byte(svg))
|
|
}
|