feat: refactor block system

Refactored from legacy layouts/partials/blox/* system to:
     - blox/<block-id>/block.html source structure
     - Hugo module mounts to layouts/partials/hbx/blocks/<block-id>/block.html
     - Resolver-based lookup
     - Support for data-driven pages and client islands
This commit is contained in:
George Cushen committed 2025-08-30 14:43:37 +01:00
1 parent acb0eb7b8a
commit 93585c868d
69 files changed
+229 -122

No files matched your search

+59 -24
View File
@@ -65,30 +65,6 @@ minify:
keepSpecialComments: true
sitemap:
changefreq: weekly
module:
hugoVersion:
min: '0.148.2'
extended: true
imports:
- path: github.com/HugoBlox/hugo-blox-builder/modules/blox-analytics
mounts:
- source: content
target: content
- source: static
target: static
- source: layouts
target: layouts
- source: data
target: data
- source: assets
target: assets
- source: i18n
target: i18n
- source: archetypes
target: archetypes
- disableWatch: true
source: hugo_stats.json
target: assets/notwatching/hugo_stats.json
security:
funcs:
getenv:
@@ -113,3 +89,62 @@ params:
date_format: 'Jan 2, 2006'
time_format: '3:04 PM'
address_format: en-us
module:
hugoVersion:
min: '0.148.2'
extended: true
imports:
- path: github.com/HugoBlox/hugo-blox-builder/modules/blox-analytics
mounts:
- source: content
target: content
- source: static
target: static
- source: layouts
target: layouts
- source: data
target: data
- source: assets
target: assets
- source: i18n
target: i18n
- source: archetypes
target: archetypes
- disableWatch: true
source: hugo_stats.json
target: assets/notwatching/hugo_stats.json
# BLOX A) Server-rendered block templates (Go HTML)
- source: blox
target: layouts/partials/hbx/blocks
includeFiles:
- '**/block.html'
# BLOX B) Client islands (compiled with js.Build/js.Batch)
- source: blox
target: assets/js/hbx/blocks
includeFiles:
- '**/*.js'
- '**/*.ts'
- '**/*.jsx'
- '**/*.tsx'
# BLOX C) Block-specific CSS
- source: blox
target: assets/css/hbx/blocks
includeFiles:
- '**/*.css'
# BLOX D) Shared assets (optional)
- source: blox/shared/js
target: assets/js/hbx/shared
- source: blox/shared/css
target: assets/css/hbx/shared
- source: blox
target: assets/img/hbx/blocks
includeFiles:
- '**/*.png'
- '**/*.jpg'
- '**/*.jpeg'
- '**/*.svg'
- '**/*.webp'
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -322,7 +322,7 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
# Feedback widget
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,4 +262,4 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
+1 -1
View File
@@ -262,7 +262,7 @@
# Published with
- id: published_with
translation: Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators.
translation: Made with {hugoblox}.
# Feedback widget
@@ -9,14 +9,9 @@
{{ range $r := site.Data.blox_aliases.renames }}
{{ $block_type = cond (eq $block_type $r.old) $r.new $block_type }}
{{ end }}
{{ $block_path := printf "blox/%s.html" $block_type }}
{{ $block_path := printf "hbx/blocks/%s/block.html" $block_type }}
{{ if not (templates.Exists (printf "_partials/%s" $block_path)) }}
{{ $block_path_community := printf "blox/community/%s.html" $block_type }}
{{ if not (templates.Exists (printf "_partials/%s" $block_path_community)) }}
{{ errorf "%s uses a `%s` blox but the `%s` blox was not found. Check the name of the blox and try again. For a custom or community blox, upload it first to `hugo-blox/blox/community/%s.html`. For an All Access blox, upload it first to `hugo-blox/blox/all-access/%s.html`" $page.File.Path $block_type $block_type $block_type $block_type }}
{{ else }}
{{ $block_path = $block_path_community }}
{{ end }}
{{ errorf "%s uses a `%s` block but the `%s` block was not found at `%s`. Check the name of the block and ensure it exists in the blox/%s/ directory." $page.File.Path $block_type $block_type $block_path $block_type }}
{{ end }}
{{/* Mermaid block: Workaround to make Hugo's .Page.Store.Set cascade up to the parent of headless pages */}}
@@ -0,0 +1,15 @@
{{/* HBX resolver: maps a section { block: "<id>", ... } to the block partial.
This resolver looks up blocks from the blox/<block-id>/block.html structure,
which are mounted to layouts/partials/hbx/blocks/<block-id>/block.html via module.yaml.
*/}}
{{- $id := .section.block -}}
{{- if not $id }}{{ errorf "HBX: section is missing 'block' key: %v" .section }}{{ end -}}
{{- $params := .section -}}
{{- $path := printf "hbx/blocks/%s/block.html" $id -}}
{{- if templates.Exists (printf "partials/%s" $path) -}}
{{ partial $path (dict "wcPage" .ctx "wcBlock" $params "wcIdentifier" ($params.id | default (printf "section-%s" $id))) }}
{{- else -}}
{{ errorf "HBX: block '%s' not found at %q" $id $path }}
{{- end -}}
@@ -0,0 +1,11 @@
{{/* Returns sections from front matter or from data. */}}
{{- $sections := .Params.sections -}}
{{- if not $sections -}}
{{/* Data-driven pages. */}}
{{- $p := .File.PathBase -}}
{{- with index site.Data.pages $p -}}
{{- $sections = .sections -}}
{{- warnf "HBX: Using data-driven sections from data/pages/%s for page %s" $p $.RelPermalink -}}
{{- end -}}
{{- end -}}
{{- return $sections -}}
@@ -1,5 +1,11 @@
{{/* Load Hugo Blox */}}
{{ range $index, $block := .Params.sections }}
{{/* Load Hugo Blox - HBX resolver-based system
Blocks live in `blox/<block-id>/block.html` and are mounted to `layouts/partials/hbx/blocks/<block-id>/block.html`.
Landing pages must specify `sections: [{ block: '<id>', ... }]`.
*/}}
{{/* Use unified sections source */}}
{{- $sections := partial "hbx/sections.html" . -}}
{{ range $index, $block := $sections }}
{{/* Do not show sections intended only for the demo site. */}}
{{ if or (not $block.demo) ($block.demo | and (eq (os.Getenv "HUGO_BLOX_DEMO") "true")) }}
{{ partial "functions/parse_block_v2" (dict "page" $ "block" $block) }}
@@ -10,21 +10,66 @@
{{ end }}
{{ partial $footer_section_path . }}
<p class="powered-by text-center">
{{ $is_sponsor := site.Params.i_am_a_sponsor | default false }}
{{ $hide_published_with_footer := site.Params.power_ups.hide_published_with | default true }}
{{ if not (and $is_sponsor $hide_published_with_footer) }}
{{ $default := "Published with {hugoblox} — the free, {repo_link}open source{/repo_link} website builder that empowers creators." }}
{{ $i18n_published_with := i18n "published_with" | default $default }}
{{ if not (findRE "{hugoblox}" $i18n_published_with) }}
{{ warnf "Please attribute Hugo Blox using `{hugoblox}` in the `published_with` text." }}
{{ $i18n_published_with = $default }}
{{/* Attribution - PLEASE DO NOT REMOVE THIS, SUPPORT OPEN SOURCE */}}
{{- if not (site.Params.i_am_a_sponsor | default false) -}}
{{- $base := "https://hugoblox.com" -}}
{{- $tid := "" -}}
{{- $ref := .Site.Params.affiliate_ref | default "" -}}
{{/* Detect Template */}}
{{ $hugoblox_file := "" }}
{{ if fileExists "hugoblox.yaml" }}
{{ $hugoblox_file = readFile "hugoblox.yaml" }}
{{ if $hugoblox_file }}
{{ $hugoblox_data := $hugoblox_file | transform.Unmarshal }}
{{ $tid = $hugoblox_data.template.id | default "" }}
{{ end }}
{{ $i18n_published_with = replace $i18n_published_with "{hugoblox}" "<a href=\"https://hugoblox.com/?utm_campaign=poweredby\" target=\"_blank\" rel=\"noopener\">Hugo Blox Builder</a>" | safeHTML }}
{{ end }}
{{/* Per-site variant selection */}}
{{- $brandOptions := slice "Hugo Blox" "Hugo Blox Builder" "Hugo Blox — Open Source" -}}
{{- $ctaWhenTemplate := slice "Duplicate this template →" "Clone this template →" "Build yours →" "Create yours →" "Build your site →" "Create your site →" -}}
{{- $ctaNoTemplate := slice "Start free →" "Build yours →" "Create yours →" "Build your site →" "Create your site →" -}}
{{- $seed := .Site.BaseURL -}}
{{- $h := md5 $seed -}}
{{- $digits := replaceRE "[^0-9]" "" $h -}}
{{- if lt (len $digits) 6 }}{{- $digits = printf "%s123456" $digits -}}{{- end -}}
{{- $n := int (substr $digits 0 6) -}}
{{- $brandText := index $brandOptions (mod $n (len $brandOptions)) -}}
{{- $ctaText := cond (ne $tid "")
(index $ctaWhenTemplate (mod $n (len $ctaWhenTemplate)))
(index $ctaNoTemplate (mod $n (len $ctaNoTemplate))) -}}
{{- $target := cond (ne $tid "")
(printf "%s/templates/%s/start" $base $tid)
(printf "%s/start" $base) -}}
{{- $brandHref := printf "%s?utm_source=site_footer&utm_medium=referral&utm_campaign=poweredby_oss&utm_content=brand_%s"
$base (cond (ne $tid "") (printf "%s" $tid) "na") -}}
{{- $ctaHref := printf "%s?utm_source=site_footer&utm_medium=referral&utm_campaign=poweredby_oss&utm_content=cta_%s"
$target (cond (ne $tid "") (printf "%s" $tid) "na") -}}
{{- if ne $ref "" -}}
{{- $brandHref = printf "%s&ref=%s" $brandHref $ref -}}
{{- $ctaHref = printf "%s&ref=%s" $ctaHref $ref -}}
{{- end -}}
<p class="powered-by text-center text-sm opacity-80 py-1">
{{ $published_with := "Made with {hugoblox}." }}
{{ $i18n_published_with := i18n "published_with" | default $published_with }}
{{ $i18n_ctaText := i18n "poweredby_button" | default $ctaText }}
{{ if not (findRE "{hugoblox}" $i18n_published_with) }}
{{ warnf "Please attribute Hugo Blox using `{hugoblox}` in the i18n `published_with` text or sponsor with All Access to remove attribution." }}
{{ $i18n_published_with = $published_with }}
{{ end }}
{{ $i18n_published_with = replace $i18n_published_with "{hugoblox}" (printf "<a class=\"underline hover:opacity-100\" href=\"%s\" target=\"_blank\" rel=\"noopener\" data-hbx=\"poweredby_brand\">%s</a>" $brandHref $brandText) | safeHTML }}
{{ $i18n_published_with = replace $i18n_published_with "{repo_link}" "<a href=\"https://github.com/HugoBlox/hugo-blox-builder\" target=\"_blank\" rel=\"noopener\">" | safeHTML }}
{{ $i18n_published_with = replace $i18n_published_with "{/repo_link}" "</a>" | safeHTML }}
{{ $i18n_published_with | replaceRE "{year}" now.Year | markdownify | emojify | safeHTML }}
{{ end }}
</p>
{{ $i18n_published_with | safeHTML }}
<a class="inline-flex items-center rounded px-2 py-0.5 border border-current ms-2 text-xs hover:opacity-100"
href="{{ $ctaHref }}" target="_blank" rel="noopener" data-hbx="poweredby_cta">{{ $i18n_ctaText }}</a>
</p>
{{- end -}}
</footer>