From f6e2c1dccc607551315e5696f639a225c4b1a075 Mon Sep 17 00:00:00 2001 From: Alex Dunmow Date: Sat, 4 Jul 2026 15:31:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20convert=20to=20codeless=20.bnp=20?= =?UTF-8?q?=E2=80=94=20templates=20+=20manifest,=20Go/templ=20deleted=20(W?= =?UTF-8?q?O-WZ-021)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- Makefile | 40 +- ascii_header.go | 46 -- ascii_header.templ | 40 -- ascii_header_templ.go | 140 ----- blocks/ascii_header.ninjatpl | 3 + {schemas => blocks}/ascii_header.schema.json | 0 blocks/blocks.yaml | 48 ++ blocks/boot_log.ninjatpl | 1 + {schemas => blocks}/boot_log.schema.json | 0 blocks/code_console.ninjatpl | 1 + {schemas => blocks}/code_console.schema.json | 0 blocks/footer.ninjatpl | 1 + {schemas => blocks}/footer.schema.json | 0 blocks/keybind_table.ninjatpl | 1 + {schemas => blocks}/keybind_table.schema.json | 0 blocks/manpage_header.ninjatpl | 1 + .../manpage_header.schema.json | 0 blocks/toc.ninjatpl | 1 + {schemas => blocks}/toc.schema.json | 0 boot_log.go | 55 -- boot_log.templ | 25 - boot_log_templ.go | 119 ---- button_override.go | 24 - button_override.templ | 20 - button_override_templ.go | 106 ---- code_console.go | 44 -- code_console.templ | 15 - code_console_templ.go | 108 ---- email_wrapper.templ | 141 ----- email_wrapper_templ.go | 434 -------------- embed.go | 59 -- footer.go | 59 -- footer.templ | 33 -- footer_templ.go | 179 ------ go.mod | 20 - go.sum | 46 -- heading_override.go | 44 -- heading_override.templ | 52 -- heading_override_templ.go | 322 ----------- helpers.go | 75 --- image_override.go | 30 - image_override.templ | 22 - image_override_templ.go | 117 ---- keybind_table.go | 56 -- keybind_table.templ | 24 - keybind_table_templ.go | 86 --- main.go | 17 - manifest.yaml | 474 +++++++++++++++ manpage_header.go | 46 -- manpage_header.templ | 12 - manpage_header_templ.go | 94 --- master_pages.json | 25 + plugin.mod | 5 +- register.go | 181 ------ registration.go | 25 - template.templ | 257 --------- template_templ.go | 541 ------------------ templates/email/terminal.ninjatpl | 11 + templates/overrides/terminal/button.ninjatpl | 1 + templates/overrides/terminal/heading.ninjatpl | 1 + templates/overrides/terminal/image.ninjatpl | 1 + templates/overrides/terminal/text.ninjatpl | 1 + templates/terminal/article.ninjatpl | 22 + templates/terminal/default.ninjatpl | 17 + templates/terminal/full-width.ninjatpl | 13 + templates/terminal/landing.ninjatpl | 20 + text_override.go | 17 - text_override.templ | 7 - text_override_templ.go | 66 --- toc.go | 50 -- toc.templ | 27 - toc_templ.go | 124 ---- 72 files changed, 662 insertions(+), 4031 deletions(-) delete mode 100644 ascii_header.go delete mode 100644 ascii_header.templ delete mode 100644 ascii_header_templ.go create mode 100644 blocks/ascii_header.ninjatpl rename {schemas => blocks}/ascii_header.schema.json (100%) create mode 100644 blocks/blocks.yaml create mode 100644 blocks/boot_log.ninjatpl rename {schemas => blocks}/boot_log.schema.json (100%) create mode 100644 blocks/code_console.ninjatpl rename {schemas => blocks}/code_console.schema.json (100%) create mode 100644 blocks/footer.ninjatpl rename {schemas => blocks}/footer.schema.json (100%) create mode 100644 blocks/keybind_table.ninjatpl rename {schemas => blocks}/keybind_table.schema.json (100%) create mode 100644 blocks/manpage_header.ninjatpl rename {schemas => blocks}/manpage_header.schema.json (100%) create mode 100644 blocks/toc.ninjatpl rename {schemas => blocks}/toc.schema.json (100%) delete mode 100644 boot_log.go delete mode 100644 boot_log.templ delete mode 100644 boot_log_templ.go delete mode 100644 button_override.go delete mode 100644 button_override.templ delete mode 100644 button_override_templ.go delete mode 100644 code_console.go delete mode 100644 code_console.templ delete mode 100644 code_console_templ.go delete mode 100644 email_wrapper.templ delete mode 100644 email_wrapper_templ.go delete mode 100644 embed.go delete mode 100644 footer.go delete mode 100644 footer.templ delete mode 100644 footer_templ.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 heading_override.go delete mode 100644 heading_override.templ delete mode 100644 heading_override_templ.go delete mode 100644 helpers.go delete mode 100644 image_override.go delete mode 100644 image_override.templ delete mode 100644 image_override_templ.go delete mode 100644 keybind_table.go delete mode 100644 keybind_table.templ delete mode 100644 keybind_table_templ.go delete mode 100644 main.go create mode 100644 manifest.yaml delete mode 100644 manpage_header.go delete mode 100644 manpage_header.templ delete mode 100644 manpage_header_templ.go create mode 100644 master_pages.json delete mode 100644 register.go delete mode 100644 registration.go delete mode 100644 template.templ delete mode 100644 template_templ.go create mode 100644 templates/email/terminal.ninjatpl create mode 100644 templates/overrides/terminal/button.ninjatpl create mode 100644 templates/overrides/terminal/heading.ninjatpl create mode 100644 templates/overrides/terminal/image.ninjatpl create mode 100644 templates/overrides/terminal/text.ninjatpl create mode 100644 templates/terminal/article.ninjatpl create mode 100644 templates/terminal/default.ninjatpl create mode 100644 templates/terminal/full-width.ninjatpl create mode 100644 templates/terminal/landing.ninjatpl delete mode 100644 text_override.go delete mode 100644 text_override.templ delete mode 100644 text_override_templ.go delete mode 100644 toc.go delete mode 100644 toc.templ delete mode 100644 toc_templ.go diff --git a/Makefile b/Makefile index 40729f7..3142e9c 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,27 @@ -# terminal — build & publish helpers (wasm .bnp workflow) +# terminal — build & publish helpers (CODELESS .bnp workflow, WO-WZ-021) # -# The theme compiles to a wasip1 reactor-mode wasm module packed into a .bnp -# artifact (`ninja plugin build`). The legacy .so path is retired (WO-WZ-015): -# the CMS is wasm-native and can no longer load .so plugins. Distribution is -# publish-to-registry -> InstallFromRegistry (hot-load, no restart). +# This theme is a CODELESS .bnp: pure declaration the host runs — blocks +# (blocks/blocks.yaml + .ninjatpl), page/system templates, template overrides, +# an email wrapper, master pages, presets, fonts and CSS in manifest.yaml. No +# Go, no templ, no plugin.wasm, no block/core dependency. `ninja plugin build` +# classifies the repo as codeless (no Go at the root) and packs the artifact +# with manifest.pb (codeless: true) and NO plugin.wasm. # # Usage: -# make build # compile wasm + pack terminal-.bnp -# make verify # validate the built .bnp (layout/abi/name/size) -# make archive-check # prove a clean `git archive HEAD` compiles to wasm +# make build # pack terminal-.bnp (codeless — no wasm) +# make verify # validate the built .bnp (loader-identical checks) +# make archive-check # prove a clean `git archive HEAD` still builds codeless # make publish # ninja plugin publish --bnp (to the active registry) -# make templ # regenerate templ Go files # make clean # remove build artefacts -.PHONY: build verify archive-check publish templ clean help +.PHONY: build verify archive-check publish clean help PLUGIN_NAME := terminal NINJA := ninja BNP := $(PLUGIN_NAME)-$(shell grep '^version' plugin.mod | sed 's/.*"\(.*\)"/\1/').bnp -# Compile to wasm and pack the .bnp (GOOS=wasip1 GOARCH=wasm, DESCRIBE probe, -# tar.zst of plugin.wasm + plugin.mod + manifest.pb + schemas/ + assets/). +# Pack the codeless .bnp (manifest.pb synthesized from plugin.mod + manifest.yaml +# + blocks/ + templates/; no wasm compile, no DESCRIBE probe). build: $(NINJA) plugin build --dir . @@ -28,12 +29,12 @@ build: verify: $(NINJA) plugin verify $(BNP) -# Prove a clean `git archive HEAD` (what publish ships) compiles to wasm. +# Prove a clean `git archive HEAD` (what publish ships) still packs codeless. archive-check: @T=$$(mktemp -d /tmp/archive-check-$(PLUGIN_NAME).XXXXXX) && \ trap 'rm -rf "$$T"' EXIT && \ git archive HEAD | tar -x -C "$$T" && \ - cd "$$T" && GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o "$$T/check.wasm" . && \ + cd "$$T" && $(NINJA) plugin build --dir . && \ echo "archive-check OK" # Publish the prebuilt .bnp to the active registry. @@ -41,19 +42,14 @@ archive-check: publish: build $(NINJA) plugin publish --bnp $(BNP) -# Regenerate templ Go files locally. -templ: - templ generate - # Remove build artefacts. clean: - rm -f $(PLUGIN_NAME)-*.bnp $(PLUGIN_NAME).so plugin.wasm + rm -f $(PLUGIN_NAME)-*.bnp plugin.wasm help: @echo "Targets:" - @echo " build Compile wasm + pack the .bnp" + @echo " build Pack the codeless .bnp (no wasm)" @echo " verify Validate the built .bnp (loader-identical checks)" - @echo " archive-check Prove a clean git archive HEAD compiles to wasm" + @echo " archive-check Prove a clean git archive HEAD packs codeless" @echo " publish Publish the .bnp to the active registry" - @echo " templ Regenerate templ Go files" @echo " clean Remove build artefacts" diff --git a/ascii_header.go b/ascii_header.go deleted file mode 100644 index 341a119..0000000 --- a/ascii_header.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "bytes" - "context" - - "git.dev.alexdunmow.com/block/core/blocks" -) - -// AsciiHeaderBlockMeta defines metadata for the TTY ascii header block. -var AsciiHeaderBlockMeta = blocks.BlockMeta{ - Key: "ascii_header", - Title: "ASCII Header", - Description: "Figlet-style TTY banner with a shell prompt line", - Source: "terminal", - Category: blocks.CategoryLayout, -} - -// AsciiHeaderData is the parsed content for the ascii_header block. -type AsciiHeaderData struct { - Title string - Prompt string - AsciiArt string -} - -// AsciiHeaderBlock renders the TTY banner. -// Content shape: {title, prompt, asciiArt}. -func AsciiHeaderBlock(ctx context.Context, content map[string]any) string { - data := AsciiHeaderData{ - Title: getString(content, "title"), - Prompt: getString(content, "prompt"), - AsciiArt: getString(content, "asciiArt"), - } - // Sensible empty-state defaults so unconfigured blocks still render - // something terminal-flavoured rather than an empty surface. - if data.Title == "" && data.AsciiArt == "" { - data.Title = "~" - } - if data.Prompt == "" { - data.Prompt = "$ " - } - - var buf bytes.Buffer - _ = asciiHeaderComponent(data).Render(ctx, &buf) - return buf.String() -} diff --git a/ascii_header.templ b/ascii_header.templ deleted file mode 100644 index ed205ba..0000000 --- a/ascii_header.templ +++ /dev/null @@ -1,40 +0,0 @@ -package main - -// figletBanner returns a small block-letter banner for the given title. -// This is intentionally simple — admins who want bespoke figlet output can -// paste it into the asciiArt field which overrides this. -func figletBanner(title string) string { - if title == "" { - return "" - } - return "/*===========================================*/\n" + - "/* " + padRight(title, 41) + " */\n" + - "/*===========================================*/" -} - -// padRight pads s with spaces on the right so the total length is at least n. -func padRight(s string, n int) string { - for len(s) < n { - s = s + " " - } - if len(s) > n { - s = s[:n] - } - return s -} - -templ asciiHeaderComponent(data AsciiHeaderData) { -
- if data.AsciiArt != "" { -
{ data.AsciiArt }
- } else { -
{ figletBanner(data.Title) }
- } -
- { data.Prompt } - if data.Title != "" { - { data.Title } - } -
-
-} diff --git a/ascii_header_templ.go b/ascii_header_templ.go deleted file mode 100644 index 24e0ee5..0000000 --- a/ascii_header_templ.go +++ /dev/null @@ -1,140 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package main - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -// figletBanner returns a small block-letter banner for the given title. -// This is intentionally simple — admins who want bespoke figlet output can -// paste it into the asciiArt field which overrides this. -func figletBanner(title string) string { - if title == "" { - return "" - } - return "/*===========================================*/\n" + - "/* " + padRight(title, 41) + " */\n" + - "/*===========================================*/" -} - -// padRight pads s with spaces on the right so the total length is at least n. -func padRight(s string, n int) string { - for len(s) < n { - s = s + " " - } - if len(s) > n { - s = s[:n] - } - return s -} - -func asciiHeaderComponent(data AsciiHeaderData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if data.AsciiArt != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
")
-			if templ_7745c5c3_Err != nil {
-				return templ_7745c5c3_Err
-			}
-			var templ_7745c5c3_Var2 string
-			templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.AsciiArt)
-			if templ_7745c5c3_Err != nil {
-				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ascii_header.templ`, Line: 29, Col: 23}
-			}
-			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
-			if templ_7745c5c3_Err != nil {
-				return templ_7745c5c3_Err
-			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
")
-			if templ_7745c5c3_Err != nil {
-				return templ_7745c5c3_Err
-			}
-			var templ_7745c5c3_Var3 string
-			templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(figletBanner(data.Title))
-			if templ_7745c5c3_Err != nil {
-				return templ.Error{Err: templ_7745c5c3_Err, FileName: `ascii_header.templ`, Line: 31, Col: 34}
-			}
-			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
-			if templ_7745c5c3_Err != nil {
-				return templ_7745c5c3_Err
-			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Prompt) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ascii_header.templ`, Line: 34, Col: 44} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if data.Title != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.Title) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ascii_header.templ`, Line: 36, Col: 44} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/blocks/ascii_header.ninjatpl b/blocks/ascii_header.ninjatpl new file mode 100644 index 0000000..0d77fd7 --- /dev/null +++ b/blocks/ascii_header.ninjatpl @@ -0,0 +1,3 @@ +{% set eff_title = title %}{% if not title and not asciiArt %}{% set eff_title = "~" %}{% endif %}
{% if asciiArt %}
{{ asciiArt }}
{% else %}
/*===========================================*/
+/* {{ eff_title|slice:":41"|ljust:41 }} */
+/*===========================================*/
{% endif %}
{{ prompt|default:"$ " }}{% if eff_title %}{{ eff_title }}{% endif %}
diff --git a/schemas/ascii_header.schema.json b/blocks/ascii_header.schema.json similarity index 100% rename from schemas/ascii_header.schema.json rename to blocks/ascii_header.schema.json diff --git a/blocks/blocks.yaml b/blocks/blocks.yaml new file mode 100644 index 0000000..361df0b --- /dev/null +++ b/blocks/blocks.yaml @@ -0,0 +1,48 @@ +# Terminal theme block definitions (codeless .bnp, WO-WZ-021). +# Keys are fully qualified (terminal:) because the codeless loader +# registers definitions verbatim — there is no PluginBlockRegistry prefixing +# in the codeless path (unlike the wasm/templ path). The qualified keys match +# the hardcoded data-block attributes and the master_pages.json references. +blocks: + - key: terminal:ascii_header + title: ASCII Header + description: Figlet-style TTY banner with a shell prompt line + category: layout + schema: ascii_header.schema.json + template: ascii_header.ninjatpl + - key: terminal:manpage_header + title: Man-page Header + description: NAME(section) ... center ... version row, like a man-page top line + category: layout + schema: manpage_header.schema.json + template: manpage_header.ninjatpl + - key: terminal:toc + title: Section TOC + description: Fixed left-rail table of contents for article pages + category: navigation + schema: toc.schema.json + template: toc.ninjatpl + - key: terminal:code_console + title: Console Block + description: Shell prompt + command + output, with language hint + category: content + schema: code_console.schema.json + template: code_console.ninjatpl + - key: terminal:keybind_table + title: Keybinding Table + description: Two-column key + action reference table + category: content + schema: keybind_table.schema.json + template: keybind_table.ninjatpl + - key: terminal:boot_log + title: Boot Log Hero + description: Sequential dmesg-style boot lines revealed one after another + category: content + schema: boot_log.schema.json + template: boot_log.ninjatpl + - key: terminal:footer + title: TTY Footer + description: EOF rule, MOTD, optional newsletter signup + category: layout + schema: footer.schema.json + template: footer.ninjatpl diff --git a/blocks/boot_log.ninjatpl b/blocks/boot_log.ninjatpl new file mode 100644 index 0000000..f459c4e --- /dev/null +++ b/blocks/boot_log.ninjatpl @@ -0,0 +1 @@ +{% if lines %}
{% for line in lines %}[ {{ forloop.Counter0 }} ] {{ line }}{% endfor %}{% set cur = cursor|default:"block" %}{% if cur != "none" %}{% if cur == "underscore" %}_{% elif cur == "pipe" %}|{% else %}█{% endif %}{% endif %}
{% endif %} diff --git a/schemas/boot_log.schema.json b/blocks/boot_log.schema.json similarity index 100% rename from schemas/boot_log.schema.json rename to blocks/boot_log.schema.json diff --git a/blocks/code_console.ninjatpl b/blocks/code_console.ninjatpl new file mode 100644 index 0000000..a42e708 --- /dev/null +++ b/blocks/code_console.ninjatpl @@ -0,0 +1 @@ +{% if command or output %}
{% if command %}
{{ prompt|default:"$" }}{{ command }}
{% endif %}{% if output %}
{{ output }}
{% endif %}
{% endif %} diff --git a/schemas/code_console.schema.json b/blocks/code_console.schema.json similarity index 100% rename from schemas/code_console.schema.json rename to blocks/code_console.schema.json diff --git a/blocks/footer.ninjatpl b/blocks/footer.ninjatpl new file mode 100644 index 0000000..6be565b --- /dev/null +++ b/blocks/footer.ninjatpl @@ -0,0 +1 @@ +{% set m = motd|default:"connection closed." %} diff --git a/schemas/footer.schema.json b/blocks/footer.schema.json similarity index 100% rename from schemas/footer.schema.json rename to blocks/footer.schema.json diff --git a/blocks/keybind_table.ninjatpl b/blocks/keybind_table.ninjatpl new file mode 100644 index 0000000..7aea67c --- /dev/null +++ b/blocks/keybind_table.ninjatpl @@ -0,0 +1 @@ +{% if rows %}{% for row in rows %}{% endfor %}
KeysAction
{% for k in row.keys|split:" " %}{% if k %}{{ k }}{% endif %}{% endfor %}{{ row.action }}
{% endif %} diff --git a/schemas/keybind_table.schema.json b/blocks/keybind_table.schema.json similarity index 100% rename from schemas/keybind_table.schema.json rename to blocks/keybind_table.schema.json diff --git a/blocks/manpage_header.ninjatpl b/blocks/manpage_header.ninjatpl new file mode 100644 index 0000000..080bfe7 --- /dev/null +++ b/blocks/manpage_header.ninjatpl @@ -0,0 +1 @@ +{% set nm = name|default:"PAGE"|upper %}{% set sec = section|default:1|integer %}{% if sec < 1 %}{% set sec = 1 %}{% endif %}{% set ver = version|default:"terminal v0.1.0" %}
{{ nm }}({{ sec }})
{{ nm }}
{{ ver }}
diff --git a/schemas/manpage_header.schema.json b/blocks/manpage_header.schema.json similarity index 100% rename from schemas/manpage_header.schema.json rename to blocks/manpage_header.schema.json diff --git a/blocks/toc.ninjatpl b/blocks/toc.ninjatpl new file mode 100644 index 0000000..20afef6 --- /dev/null +++ b/blocks/toc.ninjatpl @@ -0,0 +1 @@ + diff --git a/schemas/toc.schema.json b/blocks/toc.schema.json similarity index 100% rename from schemas/toc.schema.json rename to blocks/toc.schema.json diff --git a/boot_log.go b/boot_log.go deleted file mode 100644 index 7544b99..0000000 --- a/boot_log.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "bytes" - "context" - - "git.dev.alexdunmow.com/block/core/blocks" -) - -// BootLogBlockMeta defines metadata for the boot log hero block. -var BootLogBlockMeta = blocks.BlockMeta{ - Key: "boot_log", - Title: "Boot Log Hero", - Description: "Sequential dmesg-style boot lines revealed one after another", - Source: "terminal", - Category: blocks.CategoryContent, -} - -// BootLogData is the parsed content for the boot_log block. -type BootLogData struct { - Lines []string - Cursor string -} - -// BootLogBlock renders a sequential boot-log style hero. -// Content shape: {lines: []string, cursor: "block"|"underscore"|"pipe"|"none"}. -func BootLogBlock(ctx context.Context, content map[string]any) string { - data := BootLogData{ - Lines: getStringSlice(content, "lines"), - Cursor: getStringDefault(content, "cursor", "block"), - } - if len(data.Lines) == 0 { - return "" - } - - var buf bytes.Buffer - _ = bootLogComponent(data).Render(ctx, &buf) - return buf.String() -} - -// cursorGlyph maps the cursor selector to its rendered character. -func cursorGlyph(name string) string { - switch name { - case "underscore": - return "_" - case "pipe": - return "|" - case "none": - return "" - case "block": - fallthrough - default: - return "█" // full block - } -} diff --git a/boot_log.templ b/boot_log.templ deleted file mode 100644 index 11984ae..0000000 --- a/boot_log.templ +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "fmt" - "strconv" -) - -func bootLineStyle(idx int) string { - // Stagger reveal: 80ms per line so each delay is strictly greater - // than the previous, per UAT 13.12. - return fmt.Sprintf("animation-delay: %dms;", 80*(idx+1)) -} - -templ bootLogComponent(data BootLogData) { -
- for idx, line := range data.Lines { - - { "[ "+strconv.Itoa(idx)+" ] " + line } - - } - if cursorGlyph(data.Cursor) != "" { - { cursorGlyph(data.Cursor) } - } -
-} diff --git a/boot_log_templ.go b/boot_log_templ.go deleted file mode 100644 index f6aade1..0000000 --- a/boot_log_templ.go +++ /dev/null @@ -1,119 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package main - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -import ( - "fmt" - "strconv" -) - -func bootLineStyle(idx int) string { - // Stagger reveal: 80ms per line so each delay is strictly greater - // than the previous, per UAT 13.12. - return fmt.Sprintf("animation-delay: %dms;", 80*(idx+1)) -} - -func bootLogComponent(data BootLogData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - for idx, line := range data.Lines { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs("[ " + strconv.Itoa(idx) + " ] " + line) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `boot_log.templ`, Line: 18, Col: 41} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - if cursorGlyph(data.Cursor) != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(cursorGlyph(data.Cursor)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `boot_log.templ`, Line: 22, Col: 55} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/button_override.go b/button_override.go deleted file mode 100644 index e29cdc3..0000000 --- a/button_override.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "bytes" - "context" -) - -// TerminalButtonBlock renders a button label wrapped in [ LABEL ] brackets -// with a blinking caret on hover (via the .terminal-button class). -// -// Content shape (same as built-in button): {text, url, variant, target}. -func TerminalButtonBlock(ctx context.Context, content map[string]any) string { - text := getString(content, "text") - url := getString(content, "url") - target := getString(content, "target") - - if text == "" { - return "" - } - - var buf bytes.Buffer - _ = terminalButtonComponent(text, url, target).Render(ctx, &buf) - return buf.String() -} diff --git a/button_override.templ b/button_override.templ deleted file mode 100644 index 3a6d488..0000000 --- a/button_override.templ +++ /dev/null @@ -1,20 +0,0 @@ -package main - -func buttonHref(url string) string { - if url == "" { - return "#" - } - return url -} - -templ terminalButtonComponent(text, url, target string) { - if target == "_blank" { - - { "[ " + text + " ]" } - - } else { - - { "[ " + text + " ]" } - - } -} diff --git a/button_override_templ.go b/button_override_templ.go deleted file mode 100644 index b83ea54..0000000 --- a/button_override_templ.go +++ /dev/null @@ -1,106 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package main - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -func buttonHref(url string) string { - if url == "" { - return "#" - } - return url -} - -func terminalButtonComponent(text, url, target string) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - if target == "_blank" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs("[ " + text + " ]") - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 13, Col: 23} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("[ " + text + " ]") - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `button_override.templ`, Line: 17, Col: 23} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/code_console.go b/code_console.go deleted file mode 100644 index 18b94b1..0000000 --- a/code_console.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "bytes" - "context" - - "git.dev.alexdunmow.com/block/core/blocks" -) - -// CodeConsoleBlockMeta defines metadata for the console code block. -var CodeConsoleBlockMeta = blocks.BlockMeta{ - Key: "code_console", - Title: "Console Block", - Description: "Shell prompt + command + output, with language hint", - Source: "terminal", - Category: blocks.CategoryContent, -} - -// CodeConsoleData is the parsed content for the code_console block. -type CodeConsoleData struct { - Prompt string - Command string - Output string - Language string -} - -// CodeConsoleBlock renders an interactive-looking shell block. -// Content shape: {prompt, command, output, language}. -func CodeConsoleBlock(ctx context.Context, content map[string]any) string { - data := CodeConsoleData{ - Prompt: getStringDefault(content, "prompt", "$"), - Command: getString(content, "command"), - Output: getString(content, "output"), - Language: getStringDefault(content, "language", "shell"), - } - // Empty state: render nothing if both command and output are blank. - if data.Command == "" && data.Output == "" { - return "" - } - - var buf bytes.Buffer - _ = codeConsoleComponent(data).Render(ctx, &buf) - return buf.String() -} diff --git a/code_console.templ b/code_console.templ deleted file mode 100644 index 2c22019..0000000 --- a/code_console.templ +++ /dev/null @@ -1,15 +0,0 @@ -package main - -templ codeConsoleComponent(data CodeConsoleData) { -
- if data.Command != "" { -
- { data.Prompt } - { data.Command } -
- } - if data.Output != "" { -
{ data.Output }
- } -
-} diff --git a/code_console_templ.go b/code_console_templ.go deleted file mode 100644 index 774eda2..0000000 --- a/code_console_templ.go +++ /dev/null @@ -1,108 +0,0 @@ -// Code generated by templ - DO NOT EDIT. - -// templ: version: v0.3.1020 -package main - -//lint:file-ignore SA4006 This context is only used if a nested component is present. - -import "github.com/a-h/templ" -import templruntime "github.com/a-h/templ/runtime" - -func codeConsoleComponent(data CodeConsoleData) templ.Component { - return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { - templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context - if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { - return templ_7745c5c3_CtxErr - } - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) - if !templ_7745c5c3_IsBuffer { - defer func() { - templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) - if templ_7745c5c3_Err == nil { - templ_7745c5c3_Err = templ_7745c5c3_BufErr - } - }() - } - ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var1 := templ.GetChildren(ctx) - if templ_7745c5c3_Var1 == nil { - templ_7745c5c3_Var1 = templ.NopComponent - } - ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if data.Command != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.Prompt) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_console.templ`, Line: 7, Col: 42} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.Command) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_console.templ`, Line: 8, Col: 41} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - if data.Output != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
")
-			if templ_7745c5c3_Err != nil {
-				return templ_7745c5c3_Err
-			}
-			var templ_7745c5c3_Var5 string
-			templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.Output)
-			if templ_7745c5c3_Err != nil {
-				return templ.Error{Err: templ_7745c5c3_Err, FileName: `code_console.templ`, Line: 12, Col: 21}
-			}
-			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
-			if templ_7745c5c3_Err != nil {
-				return templ_7745c5c3_Err
-			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - return nil - }) -} - -var _ = templruntime.GeneratedTemplate diff --git a/email_wrapper.templ b/email_wrapper.templ deleted file mode 100644 index 971c08b..0000000 --- a/email_wrapper.templ +++ /dev/null @@ -1,141 +0,0 @@ -package main - -import ( - "bytes" - "context" - "fmt" - "strings" - - "git.dev.alexdunmow.com/block/core/templates" -) - -const terminalEmailRule = "================================================================================" - -const terminalSigDelim = "-- \n" - -// TerminalEmailWrapper produces a plain-text-first, monospace, 80-col centred -// email body. The first part is the plain text fallback; the HTML version -// follows it as a comment-delimited block. Inline styles only — no