core/templates/bn/head_templ.go
Alex Dunmow 868df2d761 feat: WO-PS-011 SDK templates/bn shared components
Head, engagement, toolbar templ components and validation helpers
for use by template plugins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 09:17:55 +08:00

1471 lines
69 KiB
Go

// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1001
package bn
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"strings"
"time"
"github.com/google/uuid"
)
// resolveMediaURL converts media: prefixed URLs to proper /media/ paths
// e.g., "media:abc/image.webp" → "/media/abc/image.webp"
func resolveMediaURL(url string) string {
if strings.HasPrefix(url, "media:") {
return "/media/" + strings.TrimPrefix(url, "media:")
}
return url
}
// BrandingData contains generated favicon/icon URLs
type BrandingData struct {
FaviconICO string // /brand/{id}/favicon.ico
Favicon16 string // /brand/{id}/favicon-16x16.png
Favicon32 string // /brand/{id}/favicon-32x32.png
AppleTouchIcon string // /brand/{id}/apple-touch-icon.png (180x180)
Android192 string // /brand/{id}/android-chrome-192x192.png
Android512 string // /brand/{id}/android-chrome-512x512.png
Maskable512 string // /brand/{id}/maskable-512x512.png
Master1024 string // /brand/{id}/icon-1024x1024.png
ManifestURL string // /brand/{id}/site.webmanifest
ThemeColor string
SVG string // Optional SVG pass-through
IsGenerated bool
}
// SiteSettingsData contains site-wide settings for head injection
type SiteSettingsData struct {
Title string
Description string
Favicon string
Logo string
LogoAlt string
AppleTouchIcon string
GoogleAnalyticsID string
CustomHeadScripts string
CustomBodyScripts string
MetaDescription string
DefaultOGImage string
TwitterHandle string
OGSiteName string
Branding BrandingData
AdminBypassMode string // "maintenance", "coming_soon", or "" if not bypassing
Toolbar ToolbarData
LLMsTxtEnabled bool // Whether llms.txt is enabled for AI content discovery
TurnstileSiteKey string // Cloudflare Turnstile site key for bot protection
RSSFeedURL string // URL to the RSS feed (e.g., "/rss"), empty to disable
RSSFeedTitle string // Feed title for discovery link
}
// PageMeta contains page-level SEO meta data (overrides site defaults)
type PageMeta struct {
MetaTitle string // Custom SEO title (overrides page title)
MetaDescription string // Page-specific meta description
OGTitle string // Open Graph title
OGDescription string // Open Graph description
OGImage string // Open Graph image URL
TwitterTitle string // Twitter card title
TwitterDescription string // Twitter card description
TwitterImage string // Twitter card image URL
CanonicalURL string // Canonical URL for this page
RobotsDirective string // Robots directive (e.g., "noindex, nofollow")
}
// HeadData contains all data needed to render the <head> element
type HeadData struct {
Title string
Settings SiteSettingsData
PageMeta PageMeta // Page-level SEO overrides
ThemeCSS string
ThemeMode string // "light", "dark", or "system"
PluginStyles []string // Additional stylesheet URLs
StructuredData string // JSON-LD structured data
CSSHash string // Cache-busting hash for styles.css
PageviewNonce string // Unique nonce for pageview deduplication
EngagementConfig EngagementConfig // Engagement tracking config (for blog posts)
}
// ParseEngagementConfig extracts engagement config from the document map
func ParseEngagementConfig(doc map[string]any) EngagementConfig {
config := EngagementConfig{}
engData, ok := doc["engagement_config"].(map[string]any)
if !ok {
return config
}
if v, ok := engData["page_path"].(string); ok {
config.PagePath = v
}
if v, ok := engData["page_id"].(string); ok {
config.PageID = v
}
if v, ok := engData["post_word_count"].(int); ok {
config.PostWordCount = v
}
if v, ok := engData["session_id"].(string); ok {
config.SessionID = v
}
if v, ok := engData["visitor_hash"].(string); ok {
config.VisitorHash = v
}
if v, ok := engData["is_post"].(bool); ok {
config.IsPost = v
}
return config
}
// ParseSiteSettings extracts site settings from the document map
func ParseSiteSettings(doc map[string]any) SiteSettingsData {
settings := SiteSettingsData{}
siteData, ok := doc["site_settings"].(map[string]any)
if !ok {
return settings
}
if v, ok := siteData["title"].(string); ok {
settings.Title = v
}
if v, ok := siteData["description"].(string); ok {
settings.Description = v
}
if v, ok := siteData["favicon"].(string); ok {
settings.Favicon = v
}
if v, ok := siteData["logo"].(string); ok {
settings.Logo = v
}
if v, ok := siteData["logo_alt"].(string); ok {
settings.LogoAlt = v
}
if v, ok := siteData["apple_touch_icon"].(string); ok {
settings.AppleTouchIcon = v
}
// SEO defaults
if seoData, ok := siteData["seo_defaults"].(map[string]any); ok {
if v, ok := seoData["meta_description"].(string); ok {
settings.MetaDescription = v
}
if v, ok := seoData["default_og_image"].(string); ok {
settings.DefaultOGImage = v
}
if v, ok := seoData["twitter_handle"].(string); ok {
settings.TwitterHandle = v
}
if v, ok := seoData["og_site_name"].(string); ok {
settings.OGSiteName = v
}
}
// Analytics
if analyticsData, ok := siteData["analytics"].(map[string]any); ok {
if v, ok := analyticsData["google_analytics_id"].(string); ok {
settings.GoogleAnalyticsID = v
}
if v, ok := analyticsData["custom_head_scripts"].(string); ok {
settings.CustomHeadScripts = v
}
if v, ok := analyticsData["custom_body_scripts"].(string); ok {
settings.CustomBodyScripts = v
}
}
// Branding (generated favicons)
if brandingData, ok := siteData["branding"].(map[string]any); ok {
if v, ok := brandingData["is_generated"].(bool); ok {
settings.Branding.IsGenerated = v
}
if v, ok := brandingData["favicon_ico"].(string); ok {
settings.Branding.FaviconICO = v
}
if v, ok := brandingData["favicon_16"].(string); ok {
settings.Branding.Favicon16 = v
}
if v, ok := brandingData["favicon_32"].(string); ok {
settings.Branding.Favicon32 = v
}
if v, ok := brandingData["apple_touch_icon"].(string); ok {
settings.Branding.AppleTouchIcon = v
}
if v, ok := brandingData["android_192"].(string); ok {
settings.Branding.Android192 = v
}
if v, ok := brandingData["android_512"].(string); ok {
settings.Branding.Android512 = v
}
if v, ok := brandingData["maskable_512"].(string); ok {
settings.Branding.Maskable512 = v
}
if v, ok := brandingData["master_1024"].(string); ok {
settings.Branding.Master1024 = v
}
if v, ok := brandingData["manifest_url"].(string); ok {
settings.Branding.ManifestURL = v
}
if v, ok := brandingData["theme_color"].(string); ok {
settings.Branding.ThemeColor = v
}
if v, ok := brandingData["svg"].(string); ok {
settings.Branding.SVG = v
}
}
// Admin bypass mode (for showing banner when admin bypasses maintenance/coming_soon)
if v, ok := siteData["admin_bypass_mode"].(string); ok {
settings.AdminBypassMode = v
}
// Admin editor toolbar
if toolbarData, ok := siteData["toolbar"].(map[string]any); ok {
settings.Toolbar = ParseToolbarData(toolbarData)
}
// AI Optimization settings
if aiOpt, ok := siteData["aiOptimization"].(map[string]any); ok {
if v, ok := aiOpt["llmsTxtEnabled"].(bool); ok {
settings.LLMsTxtEnabled = v
}
}
// Turnstile bot protection (from site settings)
if turnstileData, ok := siteData["turnstile"].(map[string]any); ok {
// Only set site key if Turnstile is enabled
if enabled, ok := turnstileData["enabled"].(bool); ok && enabled {
if v, ok := turnstileData["site_key"].(string); ok {
settings.TurnstileSiteKey = v
}
}
}
// RSS feed auto-discovery (injected by page handler from system page)
if v, ok := siteData["rss_feed_url"].(string); ok {
settings.RSSFeedURL = v
}
if v, ok := siteData["rss_feed_title"].(string); ok {
settings.RSSFeedTitle = v
}
return settings
}
// ParsePageMeta extracts page-level SEO metadata from the document map
// These fields override site-level defaults when set
func ParsePageMeta(doc map[string]any) PageMeta {
meta := PageMeta{}
// Page-level SEO fields are stored directly in the document root
// (matching frontend PageInfo type in web/src/store/editor.ts)
if v, ok := doc["metaTitle"].(string); ok {
meta.MetaTitle = v
}
if v, ok := doc["metaDescription"].(string); ok {
meta.MetaDescription = v
}
if v, ok := doc["ogTitle"].(string); ok {
meta.OGTitle = v
}
if v, ok := doc["ogDescription"].(string); ok {
meta.OGDescription = v
}
if v, ok := doc["ogImage"].(string); ok {
meta.OGImage = v
}
if v, ok := doc["twitterTitle"].(string); ok {
meta.TwitterTitle = v
}
if v, ok := doc["twitterDescription"].(string); ok {
meta.TwitterDescription = v
}
if v, ok := doc["twitterImage"].(string); ok {
meta.TwitterImage = v
}
if v, ok := doc["canonicalUrl"].(string); ok {
meta.CanonicalURL = v
}
if v, ok := doc["robotsDirective"].(string); ok {
meta.RobotsDirective = v
}
return meta
}
// ParseToolbarData extracts toolbar data from the document map
func ParseToolbarData(data map[string]any) ToolbarData {
toolbar := ToolbarData{}
if v, ok := data["enabled"].(bool); ok {
toolbar.Enabled = v
}
if v, ok := data["page_id"].(string); ok {
if id, err := uuid.Parse(v); err == nil {
toolbar.PageID = id
}
}
if v, ok := data["page_slug"].(string); ok {
toolbar.PageSlug = v
}
if v, ok := data["page_title"].(string); ok {
toolbar.PageTitle = v
}
if v, ok := data["post_type"].(string); ok {
toolbar.PostType = v
}
if v, ok := data["status"].(string); ok {
toolbar.Status = v
}
if v, ok := data["has_unpublished_changes"].(bool); ok {
toolbar.HasUnpublishedChanges = v
}
if v, ok := data["preview_mode"].(string); ok {
toolbar.PreviewMode = v
}
if v, ok := data["position"].(string); ok {
toolbar.Position = v
}
if v, ok := data["template_name"].(string); ok {
toolbar.TemplateName = v
}
if v, ok := data["author_name"].(string); ok {
toolbar.AuthorName = v
}
if v, ok := data["author_slug"].(string); ok {
toolbar.AuthorSlug = v
}
if v, ok := data["last_modified"].(time.Time); ok {
toolbar.LastModified = v
}
if v, ok := data["edit_url"].(string); ok {
toolbar.EditURL = v
}
if v, ok := data["settings_url"].(string); ok {
toolbar.SettingsURL = v
}
if v, ok := data["history_url"].(string); ok {
toolbar.HistoryURL = v
}
if v, ok := data["analytics_url"].(string); ok {
toolbar.AnalyticsURL = v
}
// Analytics snapshot
if v, ok := data["today_pageviews"].(int64); ok {
toolbar.TodayPageviews = v
}
if v, ok := data["pageviews_trend"].(string); ok {
toolbar.PageviewsTrend = v
}
if v, ok := data["trend_percent"].(int); ok {
toolbar.TrendPercent = v
}
// Blog-specific fields
if v, ok := data["reading_time"].(int); ok {
toolbar.ReadingTime = v
}
if v, ok := data["word_count"].(int); ok {
toolbar.WordCount = v
}
if v, ok := data["category_count"].(int); ok {
toolbar.CategoryCount = v
}
return toolbar
}
// EffectiveTitle returns the title to use in the <title> tag.
// Priority: PageMeta.MetaTitle → Page Title | Site Name → Page Title
func (d HeadData) EffectiveTitle() string {
if d.PageMeta.MetaTitle != "" {
return d.PageMeta.MetaTitle
}
if d.Settings.OGSiteName != "" && d.Title != "" {
return d.Title + " | " + d.Settings.OGSiteName
}
return d.Title
}
// Head renders the complete <head> element with site settings and plugin extensions.
func Head(data HeadData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = recordValidationCall(ctx, "Head").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.EffectiveTitle())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 395, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.CSSHash != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<link rel=\"stylesheet\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 templ.SafeURL
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs("/data/styles/styles.css?v=" + data.CSSHash)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 397, Col: 76}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<link rel=\"stylesheet\" href=\"/data/styles/styles.css\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = themeInitScript(data.ThemeMode).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<script src=\"https://unpkg.com/htmx.org@2.0.4\"></script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Settings.Branding.IsGenerated {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " <link rel=\"icon\" type=\"image/x-icon\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 templ.SafeURL
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.FaviconICO)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 405, Col: 80}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\"><link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 templ.SafeURL
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.Favicon16)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 406, Col: 90}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"><link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.SafeURL
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.Favicon32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 407, Col: 90}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"><link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 templ.SafeURL
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.AppleTouchIcon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 408, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"><link rel=\"manifest\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 templ.SafeURL
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.ManifestURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 409, Col: 65}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Settings.Branding.ThemeColor != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<meta name=\"theme-color\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.Branding.ThemeColor)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 411, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Settings.Favicon != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<link rel=\"icon\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 templ.SafeURL
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Favicon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 416, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Settings.AppleTouchIcon != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<link rel=\"apple-touch-icon\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 templ.SafeURL
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.AppleTouchIcon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 419, Col: 68}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
if data.PageMeta.MetaDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<meta name=\"description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 427, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Settings.MetaDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<meta name=\"description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 429, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.CanonicalURL != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<link rel=\"canonical\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 templ.SafeURL
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(data.PageMeta.CanonicalURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 434, Col: 58}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.RobotsDirective != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<meta name=\"robots\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.RobotsDirective)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 439, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.Settings.OGSiteName != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<meta property=\"og:site_name\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.OGSiteName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 445, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.OGTitle != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<meta property=\"og:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.OGTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 450, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.MetaTitle != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<meta property=\"og:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.MetaTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 452, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Title != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<meta property=\"og:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(data.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 454, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.OGDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<meta property=\"og:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.OGDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 459, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.MetaDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<meta property=\"og:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 461, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Settings.MetaDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<meta property=\"og:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 463, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.OGImage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<meta property=\"og:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(resolveMediaURL(data.PageMeta.OGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 468, Col: 77}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Settings.DefaultOGImage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<meta property=\"og:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(resolveMediaURL(data.Settings.DefaultOGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 470, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<meta property=\"og:type\" content=\"website\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Settings.TwitterHandle != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "<meta name=\"twitter:site\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.TwitterHandle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 478, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<meta name=\"twitter:card\" content=\"summary_large_image\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.PageMeta.TwitterTitle != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<meta name=\"twitter:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.TwitterTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 484, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.OGTitle != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<meta name=\"twitter:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var27 string
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.OGTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 486, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.MetaTitle != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<meta name=\"twitter:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.MetaTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 488, Col: 63}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Title != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<meta name=\"twitter:title\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var29 string
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(data.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 490, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.TwitterDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<meta name=\"twitter:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var30 string
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.TwitterDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 495, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.OGDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<meta name=\"twitter:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 string
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.OGDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 497, Col: 73}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.MetaDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<meta name=\"twitter:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 string
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(data.PageMeta.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 499, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Settings.MetaDescription != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<meta name=\"twitter:description\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var33 string
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 501, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.PageMeta.TwitterImage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<meta name=\"twitter:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var34 string
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(resolveMediaURL(data.PageMeta.TwitterImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 506, Col: 83}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.PageMeta.OGImage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<meta name=\"twitter:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var35 string
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(resolveMediaURL(data.PageMeta.OGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 508, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if data.Settings.DefaultOGImage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<meta name=\"twitter:image\" content=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var36 string
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(resolveMediaURL(data.Settings.DefaultOGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 510, Col: 85}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.Settings.LLMsTxtEnabled {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<link rel=\"ai-content\" type=\"text/plain\" href=\"/llms.txt\"><meta name=\"llms\" content=\"/llms.txt\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.Settings.RSSFeedURL != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var37 string
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.RSSFeedTitle + " (RSS)")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 522, Col: 97}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var38 templ.SafeURL
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.RSSFeedURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 522, Col: 131}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\"><link rel=\"alternate\" type=\"application/atom+xml\" title=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var39 string
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(data.Settings.RSSFeedTitle + " (Atom)")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 523, Col: 99}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var40 templ.SafeURL
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.RSSFeedURL + "/atom")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 523, Col: 143}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.Settings.GoogleAnalyticsID != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<script async src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var41 string
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs("https://www.googletagmanager.com/gtag/js?id=" + data.Settings.GoogleAnalyticsID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 527, Col: 103}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\"></script> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = googleAnalyticsScript(data.Settings.GoogleAnalyticsID).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = analyticsScript(data.PageviewNonce).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = EngagementScript(data.EngagementConfig).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, style := range data.PluginStyles {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "<link rel=\"stylesheet\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var42 templ.SafeURL
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(style)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 535, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.ThemeCSS != "" {
templ_7745c5c3_Err = themeStyle(data.ThemeCSS).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.Settings.CustomHeadScripts != "" {
templ_7745c5c3_Err = templ.Raw(data.Settings.CustomHeadScripts).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.Settings.TurnstileSiteKey != "" {
templ_7745c5c3_Err = turnstileScript(data.Settings.TurnstileSiteKey).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if data.StructuredData != "" {
templ_7745c5c3_Err = structuredDataScript(data.StructuredData).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</head>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// structuredDataScript renders JSON-LD structured data
func structuredDataScript(jsonLD string) templ.Component {
return templ.Raw(`<script type="application/ld+json">` + jsonLD + `</script>`)
}
// turnstileScript renders the Cloudflare Turnstile invisible bot protection
func turnstileScript(siteKey string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var43 := templ.GetChildren(ctx)
if templ_7745c5c3_Var43 == nil {
templ_7745c5c3_Var43 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "<script src=\"https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit\" async defer></script><script data-turnstile-key=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var44 string
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(siteKey)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 563, Col: 37}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\">\n\t\t(function(){\n\t\t\tvar siteKey = document.currentScript.getAttribute('data-turnstile-key');\n\t\t\tif (!siteKey) return;\n\n\t\t\t// Store active widget IDs per form\n\t\t\tvar formWidgets = new WeakMap();\n\n\t\t\t// Initialize Turnstile when API is ready\n\t\t\tfunction initTurnstile() {\n\t\t\t\tif (typeof turnstile === 'undefined') {\n\t\t\t\t\tsetTimeout(initTurnstile, 100);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Find all forms that POST to /api/* endpoints\n\t\t\t\tfunction instrumentForm(form) {\n\t\t\t\t\t// Skip if already instrumented\n\t\t\t\t\tif (formWidgets.has(form)) return;\n\n\t\t\t\t\tvar action = form.action || '';\n\t\t\t\t\tvar hxPost = form.getAttribute('hx-post') || '';\n\n\t\t\t\t\t// Only protect /api/* POST endpoints\n\t\t\t\t\tif (!action.includes('/api/') && !hxPost.includes('/api/')) return;\n\n\t\t\t\t\t// Skip exempted endpoints\n\t\t\t\t\tvar exempted = ['/api/track', '/api/webhooks/'];\n\t\t\t\t\tfor (var i = 0; i < exempted.length; i++) {\n\t\t\t\t\t\tif (action.includes(exempted[i]) || hxPost.includes(exempted[i])) return;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create container for widget\n\t\t\t\t\tvar container = document.createElement('div');\n\t\t\t\t\tcontainer.className = 'cf-turnstile-container';\n\t\t\t\t\tcontainer.style.cssText = 'position:absolute;left:-9999px;';\n\t\t\t\t\tform.appendChild(container);\n\n\t\t\t\t\t// Render invisible widget\n\t\t\t\t\tvar widgetId = turnstile.render(container, {\n\t\t\t\t\t\tsitekey: siteKey,\n\t\t\t\t\t\tsize: 'invisible',\n\t\t\t\t\t\tcallback: function(token) {\n\t\t\t\t\t\t\t// Store token in hidden field\n\t\t\t\t\t\t\tvar input = form.querySelector('input[name=\"cf-turnstile-response\"]');\n\t\t\t\t\t\t\tif (!input) {\n\t\t\t\t\t\t\t\tinput = document.createElement('input');\n\t\t\t\t\t\t\t\tinput.type = 'hidden';\n\t\t\t\t\t\t\t\tinput.name = 'cf-turnstile-response';\n\t\t\t\t\t\t\t\tform.appendChild(input);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tinput.value = token;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tformWidgets.set(form, widgetId);\n\t\t\t\t}\n\n\t\t\t\t// Instrument existing forms\n\t\t\t\tdocument.querySelectorAll('form').forEach(instrumentForm);\n\n\t\t\t\t// Watch for new forms (dynamic content / HTMX)\n\t\t\t\tvar observer = new MutationObserver(function(mutations) {\n\t\t\t\t\tmutations.forEach(function(mutation) {\n\t\t\t\t\t\tmutation.addedNodes.forEach(function(node) {\n\t\t\t\t\t\t\tif (node.nodeName === 'FORM') {\n\t\t\t\t\t\t\t\tinstrumentForm(node);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (node.querySelectorAll) {\n\t\t\t\t\t\t\t\tnode.querySelectorAll('form').forEach(instrumentForm);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\tobserver.observe(document.body, { childList: true, subtree: true });\n\n\t\t\t\t// HTMX integration: add token to request parameters\n\t\t\t\tdocument.body.addEventListener('htmx:configRequest', function(evt) {\n\t\t\t\t\tvar form = evt.detail.elt.closest('form');\n\t\t\t\t\tif (!form || !formWidgets.has(form)) return;\n\n\t\t\t\t\tvar widgetId = formWidgets.get(form);\n\t\t\t\t\tvar token = turnstile.getResponse(widgetId);\n\n\t\t\t\t\tif (token) {\n\t\t\t\t\t\tevt.detail.parameters['cf-turnstile-response'] = token;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t// Reset widgets after HTMX swap\n\t\t\t\tdocument.body.addEventListener('htmx:afterSwap', function(evt) {\n\t\t\t\t\tif (evt.detail.target.querySelectorAll) {\n\t\t\t\t\t\tevt.detail.target.querySelectorAll('form').forEach(instrumentForm);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Start initialization\n\t\t\tif (document.readyState === 'loading') {\n\t\t\t\tdocument.addEventListener('DOMContentLoaded', initTurnstile);\n\t\t\t} else {\n\t\t\t\tinitTurnstile();\n\t\t\t}\n\t\t})();\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// AdminBypassBanner renders a banner when admin is bypassing maintenance/coming_soon mode
// This should be rendered at the very start of the <body> to push down all content
func AdminBypassBanner(settings SiteSettingsData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var45 := templ.GetChildren(ctx)
if templ_7745c5c3_Var45 == nil {
templ_7745c5c3_Var45 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if settings.AdminBypassMode != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<div class=\"bg-warning text-warning-foreground px-4 py-2 text-center text-sm font-medium relative z-[9999]\"><div class=\"max-w-7xl mx-auto flex items-center justify-center gap-2\"><svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\" clip-rule=\"evenodd\"></path></svg> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if settings.AdminBypassMode == "maintenance" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<span><strong>Maintenance Mode Active</strong> — You're seeing the normal site because you're logged in as admin. <a href=\"/admin/settings?tab=status\" class=\"underline hover:no-underline\">Manage</a></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if settings.AdminBypassMode == "coming_soon" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<span><strong>Coming Soon Mode Active</strong> — You're seeing the normal site because you're logged in as admin. <a href=\"/admin/settings?tab=status\" class=\"underline hover:no-underline\">Manage</a></span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
// BodyEnd renders custom scripts and admin toolbar before </body>
func BodyEnd(settings SiteSettingsData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var46 := templ.GetChildren(ctx)
if templ_7745c5c3_Var46 == nil {
templ_7745c5c3_Var46 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = recordValidationCall(ctx, "BodyEnd").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if settings.CustomBodyScripts != "" {
templ_7745c5c3_Err = templ.Raw(settings.CustomBodyScripts).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = AdminEditorToolbar(settings.Toolbar).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var46.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// themeStyle renders the theme CSS variables
func themeStyleComponent(css string) templ.Component {
return templ.Raw(`<style id="theme-variables">` + css + `</style>`)
}
func themeStyle(css string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var47 := templ.GetChildren(ctx)
if templ_7745c5c3_Var47 == nil {
templ_7745c5c3_Var47 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = themeStyleComponent(css).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
// googleAnalyticsScript renders the GA4 inline script
func googleAnalyticsScript(gaID string) templ.Component {
return templ.Raw(`<script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', '` + gaID + `');</script>`)
}
// analyticsScript renders the built-in analytics tracking script (cookie-less)
// nonce is used for deduplication with server-side tracking
func analyticsScript(nonce string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var48 := templ.GetChildren(ctx)
if templ_7745c5c3_Var48 == nil {
templ_7745c5c3_Var48 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<script data-nonce=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var49 string
templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(nonce)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `head.templ`, Line: 724, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "\">\n\t\t(function(){\n\t\t\t// Don't track admin pages\n\t\t\tif(location.pathname.startsWith('/admin'))return;\n\n\t\t\t// CRITICAL: Capture nonce immediately while document.currentScript is valid\n\t\t\t// document.currentScript is only available during script parsing, not later\n\t\t\tvar _nonce = document.currentScript ? document.currentScript.getAttribute('data-nonce') : '';\n\n\t\t\t// Persistent session ID (survives page navigation within session)\n\t\t\tvar sid=sessionStorage.getItem('_bn_sid');\n\t\t\tif(!sid){sid=crypto.randomUUID();sessionStorage.setItem('_bn_sid',sid);}\n\n\t\t\t// ============================================================\n\t\t\t// Advanced Bot Detection - Client-Side Signals\n\t\t\t// See docs/BOT_DETECTION.md for full documentation\n\t\t\t// ============================================================\n\t\t\tvar botSignals={},botScore=0;\n\t\t\ttry{\n\t\t\t\t// 1. WebDriver property (30 points) - automation frameworks set this\n\t\t\t\tbotSignals.webdriver=!!navigator.webdriver;\n\t\t\t\tif(botSignals.webdriver)botScore+=30;\n\n\t\t\t\t// 2. CDP Detection (25 points) - detects Puppeteer/Playwright/Selenium\n\t\t\t\tbotSignals.cdp=false;\n\t\t\t\ttry{\n\t\t\t\t\tvar e=new Error();\n\t\t\t\t\tObject.defineProperty(e,'stack',{get:function(){botSignals.cdp=true;}});\n\t\t\t\t\tconsole.debug(e);\n\t\t\t\t\tif(botSignals.cdp)botScore+=25;\n\t\t\t\t}catch(x){}\n\n\t\t\t\t// 3. Plugin count (15 points) - headless often has no plugins\n\t\t\t\tbotSignals.plugins=navigator.plugins?navigator.plugins.length:0;\n\t\t\t\tif(botSignals.plugins===0)botScore+=15;\n\n\t\t\t\t// 4. Languages (15 points) - headless often has empty languages\n\t\t\t\tbotSignals.languages=navigator.languages?navigator.languages.length:0;\n\t\t\t\tif(botSignals.languages===0)botScore+=15;\n\n\t\t\t\t// 5. Chrome runtime (10 points) - real Chrome has chrome.runtime\n\t\t\t\tbotSignals.chromeRuntime=!!(window.chrome&&window.chrome.runtime);\n\t\t\t\tif(window.chrome&&!window.chrome.runtime)botScore+=10;\n\n\t\t\t\t// 6. WebGL renderer (20 points) - headless uses SwiftShader/llvmpipe\n\t\t\t\ttry{\n\t\t\t\t\tvar c=document.createElement('canvas');\n\t\t\t\t\tvar gl=c.getContext('webgl')||c.getContext('experimental-webgl');\n\t\t\t\t\tif(gl){\n\t\t\t\t\t\tvar dbg=gl.getExtension('WEBGL_debug_renderer_info');\n\t\t\t\t\t\tif(dbg){\n\t\t\t\t\t\t\tbotSignals.webglRenderer=gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL)||'';\n\t\t\t\t\t\t\tvar r=botSignals.webglRenderer.toLowerCase();\n\t\t\t\t\t\t\tif(r.includes('swiftshader')||r.includes('llvmpipe')||r.includes('software'))botScore+=20;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}catch(x){}\n\n\t\t\t\t// 7. Permission anomalies (15 points) - inconsistent API states\n\t\t\t\tif(window.Notification&&navigator.permissions){\n\t\t\t\t\tnavigator.permissions.query({name:'notifications'}).then(function(p){\n\t\t\t\t\t\tif(p.state==='denied'&&Notification.permission==='default'){\n\t\t\t\t\t\t\tbotSignals.permissionAnomaly=true;\n\t\t\t\t\t\t\tbotScore+=15;\n\t\t\t\t\t\t}\n\t\t\t\t\t}).catch(function(){});\n\t\t\t\t}\n\n\t\t\t\t// 8. Headless in Client Hints (20 points)\n\t\t\t\tif(navigator.userAgentData&&navigator.userAgentData.getHighEntropyValues){\n\t\t\t\t\tnavigator.userAgentData.getHighEntropyValues(['fullVersionList','platform']).then(function(h){\n\t\t\t\t\t\tif(h.brands&&h.brands.some(function(b){return b.brand.toLowerCase().includes('headless');})){\n\t\t\t\t\t\t\tbotSignals.headlessHints=true;\n\t\t\t\t\t\t\tbotScore+=20;\n\t\t\t\t\t\t}\n\t\t\t\t\t}).catch(function(){});\n\t\t\t\t}\n\n\t\t\t\t// 9. Automation variables (10 points each)\n\t\t\t\tif(window._phantom||window.__nightmare||window.callPhantom){botSignals.phantomjs=true;botScore+=10;}\n\t\t\t\tif(window.__selenium_unwrapped||window.__webdriver_evaluate||document.__selenium_evaluate){botSignals.selenium=true;botScore+=10;}\n\t\t\t\tif(window.__fxdriver_evaluate||window.__webdriver_script_fn){botSignals.selenium=true;botScore+=10;}\n\n\t\t\t\t// 10. Broken image dimensions (10 points) - headless often fails to render\n\t\t\t\ttry{\n\t\t\t\t\tvar img=new Image();\n\t\t\t\t\timg.src='data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';\n\t\t\t\t\tif(img.height===0){botSignals.brokenImage=true;botScore+=10;}\n\t\t\t\t}catch(x){}\n\n\t\t\t\t// Cap at 100\n\t\t\t\tbotScore=Math.min(botScore,100);\n\t\t\t\tbotSignals.score=botScore;\n\t\t\t}catch(x){botSignals.error=x.message;}\n\n\t\t\t// ============================================================\n\t\t\t// Web Vitals collection\n\t\t\t// ============================================================\n\t\t\tvar _lcp=0,_fcp=0,_cls=0,_ttfb=0;\n\t\t\tif('PerformanceObserver'in window){\n\t\t\t\ttry{\n\t\t\t\t\tnew PerformanceObserver(function(l){var e=l.getEntries().pop();if(e)_lcp=Math.round(e.startTime);}).observe({type:'largest-contentful-paint',buffered:true});\n\t\t\t\t\tnew PerformanceObserver(function(l){l.getEntries().forEach(function(e){if(e.name==='first-contentful-paint')_fcp=Math.round(e.startTime);});}).observe({type:'paint',buffered:true});\n\t\t\t\t\tnew PerformanceObserver(function(l){l.getEntries().forEach(function(e){if(!e.hadRecentInput)_cls+=e.value;});}).observe({type:'layout-shift',buffered:true});\n\t\t\t\t}catch(e){}\n\t\t\t}\n\t\t\ttry{var nav=performance.getEntriesByType('navigation')[0];if(nav)_ttfb=Math.round(nav.responseStart);}catch(e){}\n\n\t\t\t// Scroll depth tracking\n\t\t\tvar _maxScroll=0;\n\t\t\tfunction updateScroll(){\n\t\t\t\tvar h=document.documentElement.scrollHeight-window.innerHeight;\n\t\t\t\tif(h>0){var pct=Math.round((window.scrollY/h)*100);if(pct>_maxScroll)_maxScroll=pct;}\n\t\t\t}\n\t\t\twindow.addEventListener('scroll',updateScroll,{passive:true});\n\n\t\t\t// Send beacon with fallback to fetch\n\t\t\tfunction send(url,data){\n\t\t\t\tvar payload=JSON.stringify(data);\n\t\t\t\tif(!navigator.sendBeacon(url,payload)){\n\t\t\t\t\tfetch(url,{method:'POST',body:payload,keepalive:true}).catch(function(){});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Track pageview with server-side deduplication nonce\n\t\t\tfunction t(){\n\t\t\t\tvar d={\n\t\t\t\t\tp:location.pathname+location.search,\n\t\t\t\t\tr:document.referrer,\n\t\t\t\t\tsw:screen.width,\n\t\t\t\t\tsh:screen.height,\n\t\t\t\t\tvw:window.innerWidth,\n\t\t\t\t\tvh:window.innerHeight,\n\t\t\t\t\tpr:window.devicePixelRatio||1,\n\t\t\t\t\ttz:new Date().getTimezoneOffset(),\n\t\t\t\t\tcs:window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light',\n\t\t\t\t\t// Bot detection signals\n\t\t\t\t\tbs:botSignals,\n\t\t\t\t\tbsc:botScore\n\t\t\t\t};\n\t\t\t\tif(navigator.connection){\n\t\t\t\t\td.ct=navigator.connection.effectiveType||'';\n\t\t\t\t\td.cd=navigator.connection.downlink||0;\n\t\t\t\t}\n\t\t\t\t// Use the nonce captured at parse time for server-side deduplication\n\t\t\t\tif(_nonce)d.n=_nonce;\n\t\t\t\tvar u=new URLSearchParams(location.search);\n\t\t\t\tif(u.get('s'))d.st=u.get('s');\n\t\t\t\tsend('/api/track',d);\n\t\t\t}\n\n\t\t\t// Exit beacon with time on page and web vitals\n\t\t\tvar pageStart=Date.now();\n\t\t\tfunction sendExit(){\n\t\t\t\tsend('/api/track/exit',{\n\t\t\t\t\tsid:sid,\n\t\t\t\t\ttop:Date.now()-pageStart,\n\t\t\t\t\tsd:_maxScroll,\n\t\t\t\t\tlcp:_lcp,\n\t\t\t\t\tfcp:_fcp,\n\t\t\t\t\tcls:Math.round(_cls*1000)/1000,\n\t\t\t\t\tttfb:_ttfb\n\t\t\t\t});\n\t\t\t}\n\t\t\tdocument.addEventListener('visibilitychange',function(){\n\t\t\t\tif(document.visibilityState==='hidden')sendExit();\n\t\t\t});\n\n\t\t\t// Track on load\n\t\t\tif(document.readyState==='complete'){t();}else{window.addEventListener('load',t);}\n\t\t})();\n\t</script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func themeInitScript(themeMode string) templ.Component {
switch themeMode {
case "light", "dark", "system":
default:
themeMode = "light"
}
return templ.Raw(`<script>
(function(){
var c=document.cookie.match(/(?:^|; )bn-theme=([^;]*)/);
var t=c?c[1]:localStorage.getItem('bn-theme');
if(!t)t='` + themeMode + `';
var h=document.documentElement;
if(t==='system')t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';
if(t==='dark')h.classList.add('dark');
else h.classList.remove('dark');
})();
</script>`)
}
var _ = templruntime.GeneratedTemplate