buildHTMLShell wraps body HTML in the standard transactional-email chrome — header (Pad wordmark) + body + footer note + (Cloud only) marketing footer (copyright line + canonical link list). Self-hosted output is byte-equivalent to the prior inline templates so operators see no visible change after
(bodyHTML, footerNoteHTML string, cloudMode bool)
| 27 | // disclosure rendered in the small grey footer text directly above |
| 28 | // the optional marketing block. |
| 29 | func buildHTMLShell(bodyHTML, footerNoteHTML string, cloudMode bool) string { |
| 30 | var b strings.Builder |
| 31 | b.WriteString(`<!DOCTYPE html> |
| 32 | <html> |
| 33 | <head><meta charset="utf-8"></head> |
| 34 | <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1a1a1a; max-width: 560px; margin: 0 auto; padding: 40px 20px;"> |
| 35 | <div style="margin-bottom: 32px;"> |
| 36 | <strong style="font-size: 18px;">Pad</strong> |
| 37 | </div> |
| 38 | `) |
| 39 | b.WriteString(bodyHTML) |
| 40 | b.WriteString(` <hr style="border: none; border-top: 1px solid #e5e5e5; margin: 32px 0;" /> |
| 41 | `) |
| 42 | if footerNoteHTML != "" { |
| 43 | b.WriteString(` <p style="font-size: 12px; color: #999;"> |
| 44 | `) |
| 45 | b.WriteString(footerNoteHTML) |
| 46 | b.WriteString(` |
| 47 | </p> |
| 48 | `) |
| 49 | } |
| 50 | if cloudMode { |
| 51 | // Marketing footer block — full canonical link list from docs/brand.md |
| 52 | // §7, in the order the brand spec mandates: GitHub, Docs, Changelog, |
| 53 | // Contribute, FAQ, Security, Privacy, Terms, Sub-processors. The |
| 54 | // auth-page AuthFooter component carries the same nine links; emails |
| 55 | // get the same set so the brand spec's "Full parity" promise for |
| 56 | // transactional mail (§1) is honored. Codex caught a trimmed subset |
| 57 | // on first review (TASK-907 round 1). |
| 58 | year := time.Now().UTC().Year() |
| 59 | b.WriteString(fmt.Sprintf(` <p style="font-size: 11px; color: #999; line-height: 1.5; margin-top: 16px;"> |
| 60 | <a href="https://github.com/PerpetualSoftware/pad" style="color: #999; text-decoration: underline;">GitHub</a> |
| 61 | · |
| 62 | <a href="https://getpad.dev/docs" style="color: #999; text-decoration: underline;">Docs</a> |
| 63 | · |
| 64 | <a href="https://getpad.dev/changelog" style="color: #999; text-decoration: underline;">Changelog</a> |
| 65 | · |
| 66 | <a href="https://getpad.dev/contribute" style="color: #999; text-decoration: underline;">Contribute</a> |
| 67 | · |
| 68 | <a href="https://getpad.dev/faq" style="color: #999; text-decoration: underline;">FAQ</a> |
| 69 | · |
| 70 | <a href="https://getpad.dev/security" style="color: #999; text-decoration: underline;">Security</a> |
| 71 | · |
| 72 | <a href="https://getpad.dev/privacy" style="color: #999; text-decoration: underline;">Privacy</a> |
| 73 | · |
| 74 | <a href="https://getpad.dev/terms" style="color: #999; text-decoration: underline;">Terms</a> |
| 75 | · |
| 76 | <a href="https://getpad.dev/subprocessors" style="color: #999; text-decoration: underline;">Sub-processors</a> |
| 77 | </p> |
| 78 | <p style="font-size: 11px; color: #aaa; margin-top: 8px;"> |
| 79 | © %d Pad · Perpetual Software |
| 80 | </p> |
| 81 | `, year)) |
| 82 | } |
| 83 | b.WriteString(`</body> |
| 84 | </html>`) |
| 85 | return b.String() |
| 86 | } |