package main import ( "context" "github.com/a-h/templ" "git.dev.alexdunmow.com/block/core/blocks" "git.dev.alexdunmow.com/block/core/plugin" "git.dev.alexdunmow.com/block/core/templates" ) // wrap adapts a templ-returning render function to templates.TemplateFunc. // templ.Component already satisfies templates.HTMLComponent via Render. func wrap(f func(ctx context.Context, doc map[string]any) templ.Component) templates.TemplateFunc { return func(ctx context.Context, doc map[string]any) templates.HTMLComponent { return f(ctx, doc) } } // Register wires the Kindergarten theme into the host registries. The call // order matters: the system template + page templates are registered first, // then schemas are loaded before any block registrations so the schema // bindings resolve. func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error { tr.RegisterSystemTemplate(templates.SystemTemplateMeta{ Key: "kindergarten", Title: "Kindergarten", Description: "Primary-colored, hand-lettered theme for schools, daycares, toy shops, museums, and kids' publishers.", }) if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{ Key: "default", Title: "Default", Description: "Standard rounded layout with header, main, footer", Slots: []string{"header", "main", "footer"}, }, wrap(RenderKindergarten)); err != nil { return err } if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{ Key: "landing", Title: "Landing", Description: "Mascot-led hero plus stacked feature sections", Slots: []string{"hero", "main", "cta", "footer"}, }, wrap(RenderKindergartenLanding)); err != nil { return err } if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{ Key: "article", Title: "Story Page", Description: "Picture-book reading layout for blog posts and stories", Slots: []string{"header", "main", "aside", "footer"}, }, wrap(RenderKindergartenArticle)); err != nil { return err } if err := tr.RegisterPageTemplate("kindergarten", templates.PageTemplateMeta{ Key: "full-width", Title: "Full Width", Description: "Edge-to-edge gallery / classroom showcase", Slots: []string{"header", "main", "footer"}, }, wrap(RenderKindergartenFullWidth)); err != nil { return err } // Schemas must be loaded before block registrations so each Register() // call can bind to its declared schema. if err := br.LoadSchemasFromFS(Schemas()); err != nil { return err } // Theme-owned blocks (eight per spec §8). Registered unqualified; the host // addresses them as "kindergarten:" downstream. br.Register(MascotHeroBlockMeta, MascotHeroBlock) br.Register(AlphabetStripBlockMeta, AlphabetStripBlock) br.Register(ScheduleBlockMeta, ScheduleBlock) br.Register(GalleryOfArtBlockMeta, GalleryOfArtBlock) br.Register(NumbersCounterBlockMeta, NumbersCounterBlock) br.Register(StorybookQuoteBlockMeta, StorybookQuoteBlock) br.Register(BigCTABlockMeta, BigCTABlock) br.Register(FooterBlockMeta, FooterBlock) // Built-in overrides (spec §9). Each is scoped to the kindergarten template, // so they only apply when this theme is active. br.RegisterTemplateOverride("kindergarten", "heading", KindergartenHeadingBlock) br.RegisterTemplateOverride("kindergarten", "text", KindergartenTextBlock) br.RegisterTemplateOverride("kindergarten", "button", KindergartenButtonBlock) br.RegisterTemplateOverride("kindergarten", "card", KindergartenCardBlock) // Branded email wrapper. tr.RegisterEmailWrapper("kindergarten", KindergartenEmailWrapper) return nil } // DefaultMasterPages returns the default master pages that Kindergarten ships. // Per spec §7, two masters: default-master (used by default + article page // templates) and landing-master (used by landing + full-width). func DefaultMasterPages() []plugin.MasterPageDefinition { return []plugin.MasterPageDefinition{ { Key: "kindergarten:default-master", Title: "Kindergarten Default Master", PageTemplates: []string{"default", "article"}, Blocks: []plugin.MasterPageBlock{ { BlockKey: "navbar", Title: "Crayon Nav", Content: map[string]any{"menuName": "main"}, Slot: "header", SortOrder: 0, }, { BlockKey: "kindergarten:alphabet_strip", Title: "ABC Strip", Content: map[string]any{"letters": "ABCDEFGHIJ", "colorMode": "rainbow"}, Slot: "header", SortOrder: 1, }, { BlockKey: "slot", Title: "Main Slot", Content: map[string]any{"slotName": "main", "placeholder": "Page content"}, Slot: "main", SortOrder: 0, }, { BlockKey: "kindergarten:footer", Title: "Friendly Footer", Content: map[string]any{"showSignup": "true", "mascotName": "Pip"}, Slot: "footer", SortOrder: 0, }, }, }, { Key: "kindergarten:landing-master", Title: "Kindergarten Landing Master", PageTemplates: []string{"landing", "full-width"}, Blocks: []plugin.MasterPageBlock{ { BlockKey: "navbar", Title: "Crayon Nav", Content: map[string]any{"menuName": "main"}, Slot: "header", SortOrder: 0, }, { BlockKey: "kindergarten:mascot_hero", Title: "Mascot Hero", Content: map[string]any{"mascot": "pip", "headline": "Welcome to school!", "tagline": "Big fun, small humans."}, Slot: "hero", SortOrder: 0, }, { BlockKey: "slot", Title: "Main Slot", Content: map[string]any{"slotName": "main"}, Slot: "main", SortOrder: 0, }, { BlockKey: "kindergarten:big_cta", Title: "Big Yellow CTA", Content: map[string]any{"label": "Enrol today", "href": "/enrol", "colorVariant": "yellow"}, Slot: "cta", SortOrder: 0, }, { BlockKey: "kindergarten:footer", Title: "Friendly Footer", Content: map[string]any{"showSignup": "true"}, Slot: "footer", SortOrder: 0, }, }, }, } }