package main
import (
"context"
"git.dev.alexdunmow.com/block/core/templates/bn"
)
// PastelPageData carries the per-render page context.
type PastelPageData struct {
Title string
Slots map[string]string
ThemeMode string
ThemeCSS string
SiteSettings bn.SiteSettingsData
PageMeta bn.PageMeta
StructuredData string
CSSHash string
PageviewNonce string
EngagementConfig bn.EngagementConfig
}
// parsePastelPageData converts the wire-format doc map into a PastelPageData.
// It is lenient: missing fields fall back to sensible defaults so empty pages
// render without panicking.
func parsePastelPageData(doc map[string]any) PastelPageData {
title := "Untitled"
if t, ok := doc["title"].(string); ok && t != "" {
title = t
}
slots := make(map[string]string)
if s, ok := doc["slots"].(map[string]string); ok {
slots = s
}
themeCSS := ""
if tc, ok := doc["theme_css"].(string); ok {
themeCSS = tc
}
structuredData := ""
if sd, ok := doc["structured_data"].(string); ok {
structuredData = sd
}
cssHash := ""
if ch, ok := doc["css_hash"].(string); ok {
cssHash = ch
}
pageviewNonce := ""
if pn, ok := doc["pageview_nonce"].(string); ok {
pageviewNonce = pn
}
themeMode := "light"
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
themeMode = tm
}
return PastelPageData{
Title: title,
Slots: slots,
ThemeMode: themeMode,
ThemeCSS: themeCSS,
SiteSettings: bn.ParseSiteSettings(doc),
PageMeta: bn.ParsePageMeta(doc),
StructuredData: structuredData,
CSSHash: cssHash,
PageviewNonce: pageviewNonce,
EngagementConfig: bn.ParseEngagementConfig(doc),
}
}
// PastelDefault is the standard pastel-dream page template — masthead, main
// column, and footer. Used by both `default` and (with a slightly wider main)
// the `full-width` template.
templ PastelDefault(data PastelPageData) {
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/pastel-dream/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
@bn.AdminBypassBanner(data.SiteSettings)
@templ.Raw(data.Slots["header"])
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
} else {
This page is still resting. Add a block to begin.
}
@bn.BodyEnd(data.SiteSettings)
}
// PastelLanding is the marketing landing layout — full-width hero, body,
// affirmation CTA, footer.
templ PastelLanding(data PastelPageData) {
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/pastel-dream/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
@bn.AdminBypassBanner(data.SiteSettings)
@templ.Raw(data.Slots["hero"])
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
}
@templ.Raw(data.Slots["cta"])
@bn.BodyEnd(data.SiteSettings)
}
// PastelArticle is the narrow reading-room layout for editorial posts.
templ PastelArticle(data PastelPageData) {
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/pastel-dream/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
@bn.AdminBypassBanner(data.SiteSettings)
@templ.Raw(data.Slots["header"])
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
} else {
This page is still resting. Add a block to begin.
}
@bn.BodyEnd(data.SiteSettings)
}
// PastelFullWidth is the edge-to-edge layout for galleries and seasonal looks.
templ PastelFullWidth(data PastelPageData) {
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/pastel-dream/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
@bn.AdminBypassBanner(data.SiteSettings)
@templ.Raw(data.Slots["header"])
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
} else {
This page is still resting. Add a block to begin.
}
@bn.BodyEnd(data.SiteSettings)
}
// RenderPastelDefault is the registered render entry for the default template.
func RenderPastelDefault(ctx context.Context, doc map[string]any) templ.Component {
return PastelDefault(parsePastelPageData(doc))
}
// RenderPastelLanding is the registered render entry for the landing template.
func RenderPastelLanding(ctx context.Context, doc map[string]any) templ.Component {
return PastelLanding(parsePastelPageData(doc))
}
// RenderPastelArticle is the registered render entry for the article template.
func RenderPastelArticle(ctx context.Context, doc map[string]any) templ.Component {
return PastelArticle(parsePastelPageData(doc))
}
// RenderPastelFullWidth is the registered render entry for the full-width template.
func RenderPastelFullWidth(ctx context.Context, doc map[string]any) templ.Component {
return PastelFullWidth(parsePastelPageData(doc))
}