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 is the plugin entry point that wires up the Terminal system // template, its four page templates, all theme blocks, the four built-in // overrides, and the email wrapper. func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error { // 1) System template tr.RegisterSystemTemplate(templates.SystemTemplateMeta{ Key: "terminal", Title: "Terminal", Description: "Phosphor green on black terminal aesthetic with ASCII art and monospace type.", }) // 2) Page templates (exact keys + slots per spec §6 / UAT §3) if err := tr.RegisterPageTemplate("terminal", templates.PageTemplateMeta{ Key: "default", Title: "Default", Description: "Standard 80-col centered TTY layout", Slots: []string{"header", "main", "footer"}, }, wrap(RenderTerminal)); err != nil { return err } if err := tr.RegisterPageTemplate("terminal", templates.PageTemplateMeta{ Key: "landing", Title: "Landing", Description: "ASCII hero + feature grid for project README sites", Slots: []string{"hero", "main", "cta", "footer"}, }, wrap(RenderTerminalLanding)); err != nil { return err } if err := tr.RegisterPageTemplate("terminal", templates.PageTemplateMeta{ Key: "article", Title: "Article (man-page)", Description: "Man-page two-column with section nav, ideal for writeups", Slots: []string{"header", "toc", "main", "footer"}, }, wrap(RenderTerminalArticle)); err != nil { return err } if err := tr.RegisterPageTemplate("terminal", templates.PageTemplateMeta{ Key: "full-width", Title: "Full Width", Description: "Edge-to-edge for tool dashboards & status pages", Slots: []string{"header", "main", "footer"}, }, wrap(RenderTerminalFullWidth)); err != nil { return err } // 3) Schemas BEFORE block registration (per CLAUDE.md). if err := br.LoadSchemasFromFS(Schemas()); err != nil { return err } // 4) Theme blocks. Keys are unqualified at registration; address as // terminal: from master pages and page block lists. br.Register(AsciiHeaderBlockMeta, AsciiHeaderBlock) br.Register(ManpageHeaderBlockMeta, ManpageHeaderBlock) br.Register(TocBlockMeta, TocBlock) br.Register(CodeConsoleBlockMeta, CodeConsoleBlock) br.Register(KeybindTableBlockMeta, KeybindTableBlock) br.Register(BootLogBlockMeta, BootLogBlock) br.Register(FooterBlockMeta, FooterBlock) // 5) Built-in block overrides — only applied while the Terminal theme // is active. br.RegisterTemplateOverride("terminal", "heading", TerminalHeadingBlock) br.RegisterTemplateOverride("terminal", "text", TerminalTextBlock) br.RegisterTemplateOverride("terminal", "button", TerminalButtonBlock) br.RegisterTemplateOverride("terminal", "image", TerminalImageBlock) // 6) Email wrapper (plain text first, then HTML — handled by wrapper). tr.RegisterEmailWrapper("terminal", TerminalEmailWrapper) return nil } // DefaultMasterPages returns the two master pages Terminal seeds on first // load (per UAT §9): one default master covering default/landing/full-width, // and one article master that swaps in the man-page header and adds the // TOC block. func DefaultMasterPages() []plugin.MasterPageDefinition { return []plugin.MasterPageDefinition{ { Key: "terminal:default-master", Title: "Terminal Default Master", PageTemplates: []string{"default", "landing", "full-width"}, Blocks: []plugin.MasterPageBlock{ { BlockKey: "terminal:ascii_header", Title: "TTY Header", Content: map[string]any{"title": "~/projects", "prompt": "$ "}, Slot: "header", SortOrder: 0, }, { BlockKey: "navbar", Title: "Main Nav", Content: map[string]any{"menuName": "main", "style": "bracketed"}, Slot: "header", SortOrder: 1, }, { BlockKey: "slot", Title: "Main Content", Content: map[string]any{"slotName": "main", "placeholder": "// content here"}, Slot: "main", SortOrder: 0, }, { BlockKey: "terminal:footer", Title: "TTY Footer", Content: map[string]any{"motd": "connection closed.", "showSignup": true}, Slot: "footer", SortOrder: 0, }, }, }, { Key: "terminal:article-master", Title: "Terminal Article Master", PageTemplates: []string{"article"}, Blocks: []plugin.MasterPageBlock{ { BlockKey: "terminal:manpage_header", Title: "Man-page Header", Content: map[string]any{"name": "WRITEUP", "section": 1, "version": "terminal v0.1.0"}, Slot: "header", SortOrder: 0, }, { BlockKey: "navbar", Title: "Main Nav", Content: map[string]any{"menuName": "main", "style": "bracketed"}, Slot: "header", SortOrder: 1, }, { BlockKey: "terminal:toc", Title: "Section TOC", Content: map[string]any{"heading": "Sections", "items": []any{}}, Slot: "toc", SortOrder: 0, }, { BlockKey: "slot", Title: "Article Body", Content: map[string]any{"slotName": "main", "placeholder": "// article body"}, Slot: "main", SortOrder: 0, }, { BlockKey: "terminal:footer", Title: "TTY Footer", Content: map[string]any{"motd": "connection closed.", "showSignup": false}, Slot: "footer", SortOrder: 0, }, }, }, } }