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 implements 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 registers the y2k system template, its four page templates, all // theme-specific blocks, the four built-in overrides, and the email wrapper. // // Call order: page templates can be registered any time, but schemas MUST load // before any block Register call. func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error { // 1. System template tr.RegisterSystemTemplate(templates.SystemTemplateMeta{ Key: "y2k", Title: "Y2K", Description: "Liquid-chrome Y2K revival theme for indie musicians, zines, and merch shops.", }) // 2. Page templates (slots match the spec verbatim) if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{ Key: "default", Title: "Default", Description: "Chrome navbar + content + footer.", Slots: []string{"header", "main", "footer"}, }, wrap(RenderY2K)); err != nil { return err } if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{ Key: "landing", Title: "Landing", Description: "Hero metaball + marquee + CTAs.", Slots: []string{"hero", "marquee", "main", "cta", "footer"}, }, wrap(RenderY2KLanding)); err != nil { return err } if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{ Key: "article", Title: "Article / Zine Page", Description: "Narrow column with sidebar tracklist.", Slots: []string{"header", "main", "aside", "footer"}, }, wrap(RenderY2KArticle)); err != nil { return err } if err := tr.RegisterPageTemplate("y2k", templates.PageTemplateMeta{ Key: "full-width", Title: "Full Width", Description: "Edge-to-edge gradient mesh canvas.", Slots: []string{"header", "main", "footer"}, }, wrap(RenderY2KFullWidth)); err != nil { return err } // 3. Schemas first — must precede br.Register so they bind. if err := br.LoadSchemasFromFS(Schemas()); err != nil { return err } // 4. Theme-specific blocks (10 total per spec §11). br.Register(ChromeNavbarBlockMeta, ChromeNavbarBlock) br.Register(MetaballHeroBlockMeta, MetaballHeroBlock) br.Register(WaveformPlayerBlockMeta, WaveformPlayerBlock) br.Register(MarqueeBlockMeta, MarqueeBlock) br.Register(TracklistBlockMeta, TracklistBlock) br.Register(MerchCardBlockMeta, MerchCardBlock) br.Register(WebringBadgeBlockMeta, WebringBadgeBlock) br.Register(GlitterDividerBlockMeta, GlitterDividerBlock) br.Register(FooterChromeBlockMeta, FooterChromeBlock) br.Register(NFTGalleryBlockMeta, NFTGalleryBlock) // 5. Built-in overrides (only apply when y2k is the active theme). br.RegisterTemplateOverride("y2k", "heading", Y2KHeadingBlock) br.RegisterTemplateOverride("y2k", "text", Y2KTextBlock) br.RegisterTemplateOverride("y2k", "button", Y2KButtonBlock) br.RegisterTemplateOverride("y2k", "image", Y2KImageBlock) // 6. Email wrapper for branded transactional mail. tr.RegisterEmailWrapper("y2k", Y2KEmailWrapper) return nil } // DefaultMasterPages returns the master pages that y2k seeds on first load. // Block keys reference theme-owned blocks as "y2k:"; the built-in slot // block uses {"slotName":"main"} per CLAUDE.md. func DefaultMasterPages() []plugin.MasterPageDefinition { return []plugin.MasterPageDefinition{ { Key: "y2k:default-master", Title: "Y2K Default Master", PageTemplates: []string{"default", "article"}, Blocks: []plugin.MasterPageBlock{ { BlockKey: "y2k:chrome_navbar", Title: "Chrome Navbar", Content: map[string]any{"menuName": "main", "logoText": "y2k.fm"}, Slot: "header", SortOrder: 0, }, { BlockKey: "y2k:marquee", Title: "Top Marquee", Content: map[string]any{"items": []any{"new drop friday", "listen on bandcamp", "subscribe to the zine"}, "speed": "medium"}, Slot: "header", SortOrder: 1, }, { BlockKey: "slot", Title: "Main Slot", Content: map[string]any{"slotName": "main"}, Slot: "main", SortOrder: 0, }, { BlockKey: "y2k:footer_chrome", Title: "Chrome Footer", Content: map[string]any{"showWebring": true, "copyright": "(c) y2k.fm"}, Slot: "footer", SortOrder: 0, }, }, }, { Key: "y2k:landing-master", Title: "Y2K Landing Master", PageTemplates: []string{"landing", "full-width"}, Blocks: []plugin.MasterPageBlock{ { BlockKey: "y2k:metaball_hero", Title: "Metaball Hero", Content: map[string]any{"headline": "new EP out now", "sub": "liquid chrome dreams", "ctaLabel": "listen"}, Slot: "hero", SortOrder: 0, }, { BlockKey: "y2k:marquee", Title: "Marquee", Content: map[string]any{"items": []any{"* new * new * new"}, "speed": "fast"}, Slot: "marquee", SortOrder: 0, }, { BlockKey: "slot", Title: "Main Slot", Content: map[string]any{"slotName": "main"}, Slot: "main", SortOrder: 0, }, { BlockKey: "y2k:footer_chrome", Title: "Chrome Footer", Content: map[string]any{"showWebring": true}, Slot: "footer", SortOrder: 0, }, }, }, } }