themes-terminal/template.templ
Alex Dunmow 0a9b177f7c initial: theme plugin terminal
Bootstrapped during the 2026-06-06 BlockNinja consolidation. Was previously
an unversioned directory inside ~/src/blockninja-themes/terminal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 14:11:44 +08:00

258 lines
7.4 KiB
Plaintext

package main
import (
"context"
"git.dev.alexdunmow.com/block/core/templates/bn"
)
// PageData is the shared payload for every Terminal page renderer.
type PageData 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
}
func parseTerminalPageData(doc map[string]any) PageData {
title := "Untitled"
if t, ok := doc["title"].(string); ok {
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 := "dark"
if tm, ok := doc["theme_mode"].(string); ok && tm != "" {
themeMode = tm
}
return PageData{
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),
}
}
// --- Default template: header / main / footer ---
templ Terminal(data PageData) {
<!DOCTYPE html>
<html lang="en" class="dark">
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/terminal/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
<body class="terminal-page bg-background text-foreground antialiased min-h-screen flex flex-col">
@bn.AdminBypassBanner(data.SiteSettings)
<header class="w-full">
<div class="terminal-main-80 px-4">
@templ.Raw(data.Slots["header"])
</div>
</header>
<main class="terminal-main-80 w-full px-4 py-8 flex-grow">
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
} else {
<p class="terminal-mono">{ "// no content blocks assigned to this page" }</p>
}
</main>
<footer class="w-full mt-auto">
<div class="terminal-main-80 px-4">
@templ.Raw(data.Slots["footer"])
</div>
</footer>
@bn.BodyEnd(data.SiteSettings)
</body>
</html>
}
// --- Landing template: hero / main / cta / footer ---
templ TerminalLanding(data PageData) {
<!DOCTYPE html>
<html lang="en" class="dark">
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/terminal/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
<body class="terminal-page bg-background text-foreground antialiased min-h-screen flex flex-col">
@bn.AdminBypassBanner(data.SiteSettings)
<section class="w-full">
<div class="terminal-main-80 px-4 py-8">
@templ.Raw(data.Slots["hero"])
</div>
</section>
<main class="flex-grow w-full">
if main, ok := data.Slots["main"]; ok && main != "" {
<div class="terminal-main-80 px-4 py-8">
@templ.Raw(main)
</div>
}
</main>
<section class="w-full">
<div class="terminal-main-80 px-4 py-6">
@templ.Raw(data.Slots["cta"])
</div>
</section>
<footer class="w-full mt-auto">
<div class="terminal-main-80 px-4">
@templ.Raw(data.Slots["footer"])
</div>
</footer>
@bn.BodyEnd(data.SiteSettings)
</body>
</html>
}
// --- Article template: header / toc / main / footer (man-page two-column) ---
templ TerminalArticle(data PageData) {
<!DOCTYPE html>
<html lang="en" class="dark">
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/terminal/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
<body class="terminal-page bg-background text-foreground antialiased min-h-screen flex flex-col">
@bn.AdminBypassBanner(data.SiteSettings)
<header class="w-full">
<div class="terminal-main-80 px-4">
@templ.Raw(data.Slots["header"])
</div>
</header>
<div class="terminal-main-80 w-full px-4 py-6 flex-grow">
<div class="terminal-article-grid">
<aside class="w-full">
@templ.Raw(data.Slots["toc"])
</aside>
<main class="w-full">
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
} else {
<p class="terminal-mono">{ "// no content blocks assigned to this page" }</p>
}
</main>
</div>
</div>
<footer class="w-full mt-auto">
<div class="terminal-main-80 px-4">
@templ.Raw(data.Slots["footer"])
</div>
</footer>
@bn.BodyEnd(data.SiteSettings)
</body>
</html>
}
// --- Full-width template: header / main / footer, edge-to-edge ---
templ TerminalFullWidth(data PageData) {
<!DOCTYPE html>
<html lang="en" class="dark">
@bn.Head(bn.HeadData{
Title: data.Title,
Settings: data.SiteSettings,
PageMeta: data.PageMeta,
ThemeMode: data.ThemeMode,
ThemeCSS: data.ThemeCSS,
PluginStyles: []string{"/templates/terminal/style.css"},
StructuredData: data.StructuredData,
CSSHash: data.CSSHash,
PageviewNonce: data.PageviewNonce,
EngagementConfig: data.EngagementConfig,
})
<body class="terminal-page bg-background text-foreground antialiased min-h-screen flex flex-col">
@bn.AdminBypassBanner(data.SiteSettings)
<header class="w-full px-4">
@templ.Raw(data.Slots["header"])
</header>
<main class="flex-grow w-full px-4 py-6">
if main, ok := data.Slots["main"]; ok && main != "" {
@templ.Raw(main)
} else {
<p class="terminal-mono">{ "// no content blocks assigned to this page" }</p>
}
</main>
<footer class="w-full mt-auto px-4">
@templ.Raw(data.Slots["footer"])
</footer>
@bn.BodyEnd(data.SiteSettings)
</body>
</html>
}
// --- Render functions wired up in register.go via wrap(). ---
func RenderTerminal(ctx context.Context, doc map[string]any) templ.Component {
return Terminal(parseTerminalPageData(doc))
}
func RenderTerminalLanding(ctx context.Context, doc map[string]any) templ.Component {
return TerminalLanding(parseTerminalPageData(doc))
}
func RenderTerminalArticle(ctx context.Context, doc map[string]any) templ.Component {
return TerminalArticle(parseTerminalPageData(doc))
}
func RenderTerminalFullWidth(ctx context.Context, doc map[string]any) templ.Component {
return TerminalFullWidth(parseTerminalPageData(doc))
}