Skip to content
quarry Artifact [aa7d9d6052]
Login

Artifact [aa7d9d6052]

Artifact aa7d9d6052a5a97ca7ae4c2d24ad74b6fd7cfd8448013fd5df9e454620c76f88:


package markup

import (
	"fmt"
	"html/template"
	"strings"

	"craftdesign.group/quarry/internal/idn"
)

const (
	markSize = 24
	// 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))
}

// 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" bgcolor="%s" width="%d" height="%d" style="width:%dpx;height:%dpx;background:%s;border-radius:6px;padding:0;line-height:0;font-size:0;">`,
		markSize, markSize, wash, markSize, markSize, markSize, markSize, wash)
	b.WriteString(`<table role="presentation" align="center" cellpadding="0" cellspacing="0" style="margin:2px;border-collapse:collapse;">`)
	for _, row := range idn.Grid(name) {
		b.WriteString("<tr>")
		for _, c := range row {
			switch c {
			case 0:
				b.WriteString(`<td width="4" height="4" style="width:4px;height:4px;padding:0;line-height:0;font-size:0;">&nbsp;</td>`)
			default:
				col := lit
				if c == 2 {
					col = deep
				}
				fmt.Fprintf(&b, `<td bgcolor="%s" width="4" height="4" style="width:4px;height:4px;padding:0;line-height:0;font-size:0;background:%s;">&nbsp;</td>`, col, 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
}