package markup
import (
"fmt"
"html/template"
"strings"
"craftdesign.group/quarry/internal/idn"
)
const (
markSize = 28
markCell = 5
// The identicon sits on a wash of its own colour over the card (#fffcf5);
// the lit and deeper steps are idn's light-theme lightness and the
// -12% the page draws.
markLit = 0.56
markDeep = 0.44
)
// AvatarMark is an uploaded avatar for the card's header: one image at
// the address the forge serves it from. The address must be the same
// for every recipient, so a client that caches by URL fetches it once.
func AvatarMark(src, alt string) template.HTML {
return template.HTML(fmt.Sprintf(
`<img src="%s" alt="%s" width="%d" height="%d" style="display:block;width:%dpx;height:%dpx;border:0;border-radius:6px;">`,
template.HTMLEscapeString(src), template.HTMLEscapeString(alt), markSize, markSize, markSize, markSize))
}
// LogoMark is the forge's logo at the header's size: the same tile as an
// avatar, named quarry.
func LogoMark(src string) template.HTML { return AvatarMark(src, "quarry") }
// WordmarkImg is the QUARRY wordmark PNG at a display size.
func WordmarkImg(src string, w, h int) template.HTML {
return template.HTML(fmt.Sprintf(
`<img src="%s" alt="QUARRY" width="%d" height="%d" style="display:block;width:%dpx;height:%dpx;border:0;">`,
template.HTMLEscapeString(src), w, h, w, h))
}
// IdenticonMark is the name's identicon as a table of coloured cells:
// nothing to fetch, and no SVG, grid or class a mail client would strip.
// The wrapping cell carries the wash and the rounded corners.
func IdenticonMark(name string) template.HTML {
lit, deep := idn.Hex(name, markLit), idn.Hex(name, markDeep)
wash := blend(lit, "#fffcf5", 0.14)
var b strings.Builder
fmt.Fprintf(&b, `<table role="presentation" cellpadding="0" cellspacing="0" style="width:%dpx;height:%dpx;border-collapse:separate;"><tr>`+
`<td class="q-idnbg" valign="middle" bgcolor="%s" width="%d" height="%d" style="width:%dpx;height:%dpx;background:%s;border-radius:6px;padding:0;line-height:0;font-size:0;vertical-align:middle;">`,
markSize, markSize, wash, markSize, markSize, markSize, markSize, wash)
b.WriteString(`<table role="presentation" align="center" cellpadding="0" cellspacing="0" style="margin:0 auto;border-collapse:collapse;">`)
for _, row := range idn.Grid(name) {
b.WriteString("<tr>")
for _, c := range row {
switch c {
case 0:
fmt.Fprintf(&b, `<td width="%d" height="%d" style="width:%dpx;height:%dpx;padding:0;line-height:0;font-size:0;"> </td>`, markCell, markCell, markCell, markCell)
default:
col := lit
if c == 2 {
col = deep
}
fmt.Fprintf(&b, `<td bgcolor="%s" width="%d" height="%d" style="width:%dpx;height:%dpx;padding:0;line-height:0;font-size:0;background:%s;"> </td>`, col, markCell, markCell, markCell, markCell, col)
}
}
b.WriteString("</tr>")
}
b.WriteString(`</table></td></tr></table>`)
return template.HTML(b.String())
}
// blend is fg at alpha over bg, both #rrggbb.
func blend(fg, bg string, alpha float64) string {
var f, g [3]int
fmt.Sscanf(fg, "#%02x%02x%02x", &f[0], &f[1], &f[2])
fmt.Sscanf(bg, "#%02x%02x%02x", &g[0], &g[1], &g[2])
out := "#"
for i := range f {
out += fmt.Sprintf("%02x", int(float64(f[i])*alpha+float64(g[i])*(1-alpha)+0.5))
}
return out
}